DEV Community

Laurentiu Raducu
Laurentiu Raducu

Posted on

Microservices are bad for your mental health

Short, tldr version here: https://www.youtube.com/watch?v=wtXrOXYO0LA
Long version below 👇

Last month I wrote a hit piece on software in general, venting my decade-long frustrations as a user of commercial software. While I briefly touched on today’s topic, I didn’t delve into it deeply. So, I plan to share my experiences with microservices and explain why this trend is causing burnout and immense frustration among developers.

There are many myths surrounding microservices. Some people advocating for them say that scaling-related problems are solved by following this architecture. Some say that it enables high development speed, while others exaggerate how great it is in comparison to monoliths. These myths were propagated by tech gurus and influencers, people who don’t usually write code to create successful products, but rather philosophize about different tech topics. They don’t have the time to develop products, as their role as professional conference speakers occupies most of their time.

Engineers mistakenly look for answers formulated by these philosophers (or thought leaders). Instead of thinking critically about problems and trying to simulate what the outcome will be, they will just go to a conference, listen to these professional conference speakers, and start idolizing the nice stories they’ve heard.

As a consultant working mostly with large enterprises, I am amazed to see how huge budgets are being allocated to moving away from monolithic to microservice architectures. The motivation seems always to be related to scaling, but the only thing that scales in the end is their cloud costs. I tried to track down some of these initiatives, and most of them come from non-technical CTOs trying to impress the board members by shoving the latest buzzwords in their faces. A limited subset of these initiatives are influenced by visits from tech gurus, and only in one case was this architecture style a promising change and came organically from the engineering teams.

Why Microservices Suck?

In my latest blog post I got some really nasty answers, as well as some death threats on my YouTube channel, therefore I have to give you the following disclaimer. In this post, I am going to give my personal opinion based on anecdotes from my experience as a consultant. Although this might seem controversial, my aim is to raise awareness around the pitfalls of this architectural choice. My aim is not to insult or challenge anyone personally, so don’t take this post too seriously. If you don’t agree and you cannot supply constructive criticism, just stop reading, it’s that simple.

Microservices do not scale better than monoliths

Established tech voices always tell you that rather than scaling vertically, by adding hardware resources to a server, like it’s being done in the case of monoliths, it’s better to do it horizontally, by adding another server. This is often false. (or “This is not always true.”)

Contrary to what you’ve heard so far from the biggest voices in tech, there is no superiority when it comes to scaling with neither of these architectures. Let’s break this down with a clear example:

Imagine you have a monolith service for an e-commerce app, which is containerized and deployed on two machines, with a load balancer balancing the requests between the two. You are convinced by a tech guru that in order to scale, you must convert everything to microservices. You decide to split your monolith into multiple services:

  • Authentication
  • Products
  • Orders
  • Payments
  • Stock

Of course, you won’t go with these simple names, so you get your team to brainstorm some cool names for your services, inspired by either comic books, mythology, or astronomy. Since you are also agile, you need to add story points to this effort, so you will end up losing some time in endless debates about what the difference between 2, 3, and 5 story points is, and you finish by adopting the t-shirt size estimation method.

After all this effort, you end up with two machines for each service and a load balancer for each, which means a total of 10 machines (without the gateway service), a huge amount of bloatware given by the many HTTP requests going between services, and a new living hell for maintaining the contracts between these new services. How is this scalable if, for each new service, you need to provision a new machine (or two), find the best strategy for balancing the load, and make sure the contracts won’t break when developing new features?

Microservices are not testable

As humans, we are not able to predict what a change is going to cause in a complex system. Without proper simulations or testing against a system that replicates the real one, you cannot predict what will happen. In software, testing is absolutely critical for ensuring software quality, we all know that. However, in the case of microservices, testability is often lower than in the case of monoliths.

On the one hand, you have the tests developed for the service itself. Let’s stick with the same e-commerce app, where we want to define tests for our payments service. Devs will write unit and system tests, which will assess how individual components behave when fed certain inputs. They need to make a change to adapt the quantity field, which is the field that is supposed to hold the information on the quantity of the product that the customer needs to pay for, from integer to double. After deploying the change, production gets broken, and the company loses thousands of dollars for not allowing customers to buy anything for a few minutes.

Even though the tests were green, the integration between the service supplying the quantity and the payment service wasn’t tested properly. The quantity was still sent as an integer instead of being adapted to double, to match the expectation of the consumer (payment service).

There are ways to address this issue of testing the integration of the services, but none of these ways are easy enough, and require constant maintenance. Contract testing is one of the options. In contract testing, you define the consumer’s HTTP request and the provider’s expected response. You send the request and the expectation to a third-party service (in the case of Consumer-driven Contract Testing from Pact, the broker), and this service will execute the request against the provider, and check the response against the expectation. This sounds great in theory, but:

Both the consumer and provider need to make assumptions about the behavior of specific fields in the contract that are not explicitly enforced. For example, a consumer might expect a “quantity” field to be always positive, while the provider might allow zero to indicate “out of stock”. The consumer assumes it’s always positive because that fits its usage pattern. Meanwhile, the provider assumes it needs the flexibility of zero for inventory management. In the context of verifying the schema, this kind of assumptions would never be checked.
The effort for maintaining these tests is huge, on one hand, in the case of teams that are in early-stage development. It doesn’t provide flexibility in dealing with the rapidly evolving interfaces. On the other hand, legacy systems might need to invest in performing big amounts of refactoring in order to enable this type of testing, which is impractical for most of the organizations.
The learning curve is steep – devs will have to fully understand the concept, spend time developing proof of concepts, test their assumptions, which drains a lot of time and energy from focusing on critical business requirements.
You can always default to end-to-end tests, which will give you the confidence and fidelity that the changes won’t break your system. However, these tests are slow, and in the context of microservices, in a really entangled architecture, devs will spend a lot of time defining, maintaining, debugging and even running those tests. It’s just not worth it to wait hours and hours for the tests to end in order to deploy a change.

Development is very slow for microservices

You’ve heard that right! It takes a lot of time for a developer to push a change in this architecture, which leads to sluggish development cycles. A primary reason for this is the increased cognitive load and context switching that developers face. With numerous independent services, each with its own codebase, dependencies, and deployment processes, developers must constantly switch between different contexts to understand and modify the system. This continuous mental juggling can significantly impede development speed, especially for complex systems with a multitude of microservices.

Furthermore, debugging and troubleshooting become more cumbersome in a microservice environment. When an issue arises, pinpointing the root cause can be time-consuming as developers need to trace the interaction path through multiple services. This process involves analyzing logs, tracking requests, and potentially reproducing the issue in different environments, further adding to the development time. The distributed nature of microservices can also make it challenging to maintain a holistic view of the system, leading to inefficiencies and delays in resolving problems.

All in all, I think microservices are bad for your mental health as an engineer. What appears to be a huge jump in quality of life, leading to fast development, better flexibility and improved scalability, turns out to be a huge pain. Constant context switching, increased stress from observing and understanding all the interactions, and the hazardous nature of adding changes to your services will easily make you burn out and lose motivation for your work.

Top comments (0)