A blog about Software Engineering topics with a focus on data driven development and functional programming.
I have worked most of my career as either a self employed freelancer or in a small company I founded together with other developers. In addition to building software and managing projects I was responsible to work the sometimes rather boring yet necessary bureaucracy stack. One of the things I had to do was financial planning including contrasting income and cost. This would also include setting the hourly rates that I had to charge to our customers. After the big recession in 2023 has also reached the IT industrie the willingness to pay high rates to contractors has - understandably - diminished. But still, I sometimes wonder how companies expect to find any contractor given the somewhat insulting rates these companies offer.
As a quick Disclaimer, I worked on several projects for different customers in the past years and the statements in this article are based on the experiences I have collected in this time. And as I live in Germany all calculations (i.e. taxes) are based on the German laws and cost living in Germany.
Many of the job offerings sound something like this:
We are looking for a freelance Software Developer with at least eight years of experience with [insert some technologies] and profound knowledge of [insert more tech]. Remote work possible with up to two weeks a month on site. We offer a generous payment of up to 60€ per hour (including all expenses).
The last sentence usually means that they are not paying extra for your travelling cost and they expect you not to charge the time spent travelling. And most times the "up to" rate is only when working on site. The time working remote is usually about 10-20% below that (in this example it would then be 48€ per hour).
The "high" rate of 60€/48€ per hour first may seem a lot to those who are not doing freelance work but are instead in a fulltime employment. But please let me explain why this is not the case and why I would consider such low rates as outrageous.
Lets start with a simple calculation. A year has in average 365 days with 52 weeks per year with weekends as work free days. In addition here in Germany we have an average of 11 official holidays. Then we would like to have 30 days of holidays (common in Germany for fulltime employments) and about 25 sick days on which we cannot work (if you have children the number of sickdays will be a lot higher!).
what | days |
---|---|
Year Total | 365 |
Weekend days | 104 |
German Holidays | 11 |
Work Holidays | 30 |
Sick Days | 25 |
Total remaining working days: 195
Lets assume that a freelancer would work all those days full time (8 hours per day). This would indeed yield in a yearly income of 74.880€ assuming the hourly rate of 48€. This doesn't sound too bad compared to the average income in Germany. Comparing it with the income of full time employed software developers it would not be top notch but still not too bad. No problem there, right?
Well, unfortunately quite a few things are still missing to get a realistic calculation.
First, the result of a total of 195 working days does not fit. First, you cannot not sell all working days to customers. You still need time to do the "non productive" work. This includes tasks like accounting, acquiring new customers, meeting with a potential customer and creating an offering for the project and many other things. Also as a freelancer in the software development domain you need to keep up and spend time on learning new skills and technologies. From my experience, you should spend about 5-10% of your time building new skills. Dealing with all the other "non productive" tasks will again take about 10-20% of your time. Lets look at the result taking this into account:
195 days - 10% education - 20% non productive work = 137 days (expected case)Well, taking these remaining "productive" working days and selling them to customers will only yield 52.600€.
But that is only the total income, not the money you can put in your pocket at the end of the year. Now lets talk about expenses. Fortunately working as a software developer does not require expensive machinery or large production halls. But still, you need at least a computer and a phone, internet access, licenses (software, developer membership, ...) an office space (including furniture), some insurance, a tax consultant and other fees you have to pay. Lets sum up the minimal cost:
expense | monthly expense | yearly expense | ||
---|---|---|---|---|
office rent | 300€ | 3.600€ | ||
internet & phone | 100€ | 1.200€ | ||
mobility | 50€ | 600€ | ||
hardware | 2.000€ | |||
licenses | 500€ | |||
insurance | 1.500€ | |||
other purchases | 1.000€ | |||
other fees | 1.000€ |
Total expenses: 11.400€
Thats a lot. Now you can argue that you could save some money and keep expenses lower than the listing above. But even if you are thrifty or work exclusively from home you still need to pay most of these expenses. After these expenses you will be left with the following revenue after expenses:
52.600€ - 11.400€ = 41.200€And you still cannot take this money and spend it on toys and chocolate. Because you need at least to use some of the revenue and put it aside for bad times. Because there is no guarantee that you will always have a paying client. You will have time between contracts in which you will have no income. You may get sick long term. You may become a parent and want to spend time with your family. And a thousand more things that will leave you with no income over an extended time period. As such I would recommend to take at least 10% of your revenue and put it aside. Which will settle the total amount of money left each year to
41.200€ - 10% = 37.000€
Now lets pay social fees. In Germany you have at least to pay your health insurance and you really should pay pension insurance. A fulltime employee in Germany pays about 20% of his gross income for those. And the employer pays an additional 20% of the employees salary for this. As a freelancer you need to pay both the employee and the employer share of the insurance fees. And then of cause you need to pay your personal taxes for the salary. Again lets to some final math:
37.000€ - 40% (health + pension insurance) - taxes = 18.000€ overall net income
Thats not much. In comparison a full time employee earning a gross total salary of 70.000€ would get the overall net income of about 43.000€. That is more than twice as much!
Now the interesting question: What hourly rate do you need to get the same net income as a freelancer given the expenses and cost as described above? Doing some backwards calculations will yield an hourly rate of 108€. Thats more than twice the rate the job offering advertises!
Taking the hourly rates many companies offer in their job anouncements, it is simply impossible to earn the money required to feed a family here in Germany. And I might add that the companies offering such low rates are by far not just located in countries that migh have lower living cost as compared to Germany. There are more than enough companies that reside within Germany offering such insulting rates.
For everyone who might think of becomming a Freelancer, please do not just accept such offers. Think twice and calculate the rates you need to make a living. Otherwise you won't be able to pay your rent and the companies will continue and even intensify such practices.
Please note that all example calculations here are quite rough and real world calculation that i.e. a tax consultant would do will vary from the numbers in the example. Yet the precision in the example is more than enough to present my actual point on the topic.
I used following code snippets to calculate the numbers I have been talking about in this post. Please feel free to use the code to do some calculations by yourself and change the numbers according to your needs. For example the calculation does not take into account stuff like other Steuerklassen (~tax levels) or having children.
(def low-hourly-rate 48)
(def working-hours-per-day 8)
(def days-per-year 365)
(def weekend-days-per-year 104)
(def german-holidays-per-year 11)
(def work-holidays 30)
(def sick-days 25)
(def education-proportion 0.1)
(def non-productive-work-proportion 0.2)
(def expenses
[["office rent" :monthly 300]
["internet & phone" :monthly 100]
["mobility" :monthly 50]
["hardware" :yearly 2000]
["licenses" :yearly 500]
["insurance" :yearly 1500]
["other purchases" :yearly 1000]
["other fees" :yearly 1000]])
(def revenue-reserve-proportion 0.1)
(def health-insurance-employer-rate 0.073)
(def health-insurance-employee-rate 0.073)
(def pension-insurance-employer-rate 0.093)
(def pension-insurance-employee-rate 0.093)
(def unemployment-insurance-employee-rate 0.013)
(def unemployment-insurance-employer-rate 0.013)
(def bbg-pension-2023 90600)
(def bbg-health-2023 62100)
(defn- social-fee
[income rate bbg]
(if (>= income bbg)
(* bbg rate)
(* income rate)))
(defn health-insurance-fee-employee
[income]
(social-fee income health-insurance-employee-rate bbg-health-2023))
(defn health-insurance-fee-employer
[income]
(social-fee income health-insurance-employer-rate bbg-health-2023))
(defn pension-insurance-fee-employee
[income]
(social-fee income pension-insurance-employee-rate bbg-pension-2023))
(defn pension-insurance-fee-employer
[income]
(social-fee income pension-insurance-employer-rate bbg-pension-2023))
(defn unemployment-insurance-fee-employee
[income]
(social-fee income unemployment-insurance-employee-rate income))
(defn unemployment-insurance-fee-employer
[income]
(social-fee income unemployment-insurance-employer-rate income))
(defn income-tax
"Based on German income tax of 2023 for Steuerklasse I.
Source:
https://www.bmf-steuerrechner.de/ekst/eingabeformekst.xhtml?ekst-result=true
"
[zve]
(cond
(< zve 10908)
0.0
(< zve 15999)
(let [y (/ (- zve 10908) 10000)]
(* (+ (* 979.18 y) 1400) y))
(< zve 62809)
(let [z (/ (- zve 15999) 10000)]
(+ (* (+ (* 192.59 z) 2397) z) 966.53))
(< zve 277825)
(- (* zve 0.42) 9972.98)
(>= zve 277826)
(- (* zve 0.45) 18307.73)))
(defn freelancer-salary-gross->net
"Get the net salary of a freelancer from her gross salary.
This is salary - social insurance - taxes. Because freelancers
don't have a regular employer they have to pay the employer part
of the social insurance as well. But as usual for the income tax
the emplyee part of social insurance gets mostly subtracted before
calculating the taxes. (otherwise the employee would have to pay taxes
for the social fees which would be kinda strange)."
[salary]
(let [employee-fees (+
(health-insurance-fee-employee salary)
(pension-insurance-fee-employee salary)
(unemployment-insurance-fee-employee salary))
employer-fees (+
(health-insurance-fee-employer salary)
(pension-insurance-fee-employer salary)
(unemployment-insurance-fee-employer salary))]
(- salary
employee-fees
employer-fees
(income-tax (- salary employee-fees)))))
(def possible-working-days
(- days-per-year
weekend-days-per-year
german-holidays-per-year
work-holidays
sick-days))
(def payed-working-days
(- possible-working-days
(* possible-working-days education-proportion)
(* possible-working-days non-productive-work-proportion)))
(def total-expenses
(reduce
(fn [sum [_ interval amount]]
(+ sum
(if (= :monthly interval)
(* 12 amount)
amount)))
0
expenses))
(defn total-income
[working-days hourly-rate]
(* working-days 8 hourly-rate))
(defn -%
"Remove the given percentage of the given amount."
[amount percent]
(* amount (- 1.0 percent)))
(-> (total-income payed-working-days low-hourly-rate)
(- total-expenses)
(-% revenue-reserve-proportion)
(freelancer-salary-gross->net)
) ; ~> 18.000€ salary
;; increase the hourly rate to 80€
(-> (total-income payed-working-days 80)
(- total-expenses)
(-% revenue-reserve-proportion)
(freelancer-salary-gross->net)
) ; ~> 31.000€ net salary
;; increase the hourly rate to 108€
(-> (total-income payed-working-days 108)
(- total-expenses)
(-% revenue-reserve-proportion)
(freelancer-salary-gross->net)
) ; ~> 43.000€ net salary
Published: 2024-01-22
Tagged: freelancer contract work business
Modern webapps are complex. To reduce the complexity it's a common abstraction to split them into a client side application that runs in the browser and a server side application running in the mysterious cloud. This abstraction is very rough and does increasingly often not fit reality. More and more frameworks keep growing that promise to be a full-stack client and server solution to build webapps and boundaries between client and server get blurry. This trend does not only promise quicker and cheaper development cycles but it also follows the idea to write code once and run it everywhere. In the domain of webapps, Javascript and derivates like Typescript became the language of choice because they can be run both in the web browser and on the server side. They are also capable to build native mobile apps with the same full stack frameworks that are used to build the server side application and the webapp.
I don't like the idea to write code once and run it everywhere. I don't like the idea to use a single framework to build an application for very different target platforms.
Using a single language, framework and code base seems very appealing in the beginning and for very small and limited applications this approach is very fine. But the larger an application grows the more complex it will become. Being able to run the application on more than one target platform with "no worries about how to manage and sync state" adds even more complexity.
When a new product is planned, the choice which language and frameworks will be used is often driven by keeping down cost, getting a short time to marked, the current trends of technology and availability of developers already familiar with the technologies. All of those reasons are very understandable and they should not just get disregarded. Yet they will many times end up in choosing the technologies I described above with their limitations and problems.
When building a new product - or rather any software driven product - the focus should lay on what the product does. And in almost all cases the answer can be broken down to "read data, process it, present the result, repeat".
With this rather trivial insight I would suggest to build the product with the data as the smallest building block. Ensure the data is treated the same way in all parts of the application. This applies to the technical representation of the data and also on how to fetch and change it.
A colleague of mine wrote a blog article about the Frontend Architecture of modern webapps and he states, that many of the techniques learned in the past years in software development on the server is simply dropped and forgotten when looking on how we build modern frontends. And he is right. Presentation, data fetching, local state, data processing, etc. is all mangled together and the worst of these problems probably being bad state management. Assuming the webapp is build using a library like react, state can be placed and managed inside a UI-Component, get passed down from a parent UI-Component or placed into some (more or less) global state container.
This necessarily leads to problems like the following:
Based on these problems it is clear that the how and where state is placed, accessed and changed needs a clear structure. But when talking about state, let's not forget the very simple fact that state is just data.
Nikita Prokopov describes the pain points of data management and his idea of a modern architecture that would solve at least some of the challenges in his blog post The web after tomorrow. To quickly summarize the key ideas of the web after tomorrow related to the problem of state management, data and how we manipulate it should be consistent for a software system.
Most traditional webapps poll the server for some piece of data, i.e. using a REST-like API request. The server then processes the request (authentication, authorization, input validation) and collects the data (i.e. from a database system) and sends it back to the client by transforming the data into JSON or a similar on the wire format. The client (the webapp) takes the data, decodes and transforms it into a local format (i.e. objects, arrays, scalars) and presents the data to the user. In the same way, a client can send a request with some arguments to the server to update data. Again the server will process the request and write data to the database.
With this conventional approach data can become inconsistent very easily. For example, a modern webapp might have several elements that require their own datasets. Following the idea of data encapsulation and responsibilities as modeled in the Object Oriented paradigm - many modern webapps are build using this principle - , each of those elements will fetch and manage their data independently. This works well as long as data does not overlap between the different elements. If one element fetches data periodically, it might has received a newer version of the data from the server than another element which has fetched the data only once it was added to the DOM. Or as another example, a webapp might have a top level navigation bar with a widget showing the name and profile picture of the user. Another element of the webapp is a form to update the user information. When a user updates his profile picture using the form, the element inside the navigation bar will not automatically get updated because it only fetched the profile picture once it was mounted, effectively introducing inconsistent data in the webapp.
There are of cause several options to go on with the problem and in the example of the user's profile picture the data inconsistency might be considered non critical. Implementing some kind of update mechanism would be surely possible. I.e. the development team could add a message bus and each element of the webapp can subscribe to messages that might inform them to update their data. But this would be complex since both elements that change data must send a message to the bus and other elements must subscribe to it. And even if no subscription is missing and all events are correctly put onto the bus, the user profile image element would need to fetch the new picture from a server resulting in unnecessary requests and the update to that element would always be late.
Nikita suggests two simple ideas to avoid the problems. He proposes a hierarchy of top-down layered data views as described in Figure 1. The single source of truth, i.e. the database, contains all data relevant for the software system. On top of this data, several layers of filtering happens and trough these layers each client has its own view on the data available to this client. This filtering can for example include per tenant filtering and access control filters but also filters that are requested by the client itself (i.e. something like pagination).
The second piece is a unified mechanism to fetch and update data. Instead of encapsulating state and the logic to manipulate state in an Object Oriented style inside of the elements of the webapp, each element is kept stupid. The webapp is build with a single global data store and a single function exists to update the data. Changes are persisted in the data store and synced to the server.
The details of the data management mechanism are complex and will require an article for itself some time in the future.
Using this approach would allow us developers to build high quality webapps that are easy to maintain and extend. While the initial complexity of creating software systems this way might seem high at first, it will reduce complexity a lot even on short term.
I will write about the details of how such a system can be build and how the unified data management mechanism might look like in a future article.
Published: 2023-08-13
Tagged: architecture webapp