Let’s (not) break up the monolith - Part 1
Time and again clients approach my colleagues and me with the request that they want to break up their monolith into microservices and they ask us how to do this best. Apparently, they are convinced that breaking up the monolith into microservices will solve some big problems they had for a long time.
Often, they are not willing to discuss if the measure will help to solve the problem they think it will solve. They only want advice at the technical design and implementation level.
I find this approach somewhat puzzling because based on my experiences microservices rarely solve the problems people think they do – especially if they are introduced without changing anything else. I already wrote a complete blog post series discussing the widespread fallacies regarding microservices. In that series, I discussed what problems microservices do not solve, which problems they can solve (if done right) and what we can do instead if microservices are not what we need.
But still: Here we are. The client decided they need (or rather: want?) microservices. They are convinced that breaking up their monolith into microservices will solve the problems they had with their monolith. If you ask them why they want to break up their monolith, you typically get two responses:
- The monolith has become a big ball of mud and is barely maintainable anymore. Microservices will improve maintainability.
- They need to improve their time to market. Changing the monolith takes too long. Microservices will improve time to market.
Often these responses are given with an attitude of: “Isn’t that obvious, you dummy? Why do you even ask?”
Well, let us look at these two responses and see if microservices will help them to solve the aforementioned issues. As this post would be very long as a single post, I decided to split it up in two posts. This post will discuss the first response. The next post will discuss the second response and sum it all up.
The big ball of mud issue
Since the beginnings of microservices roughly 10 years ago, I hear the same claim all the time: Monolith equals big ball of mud equals unmaintainable – and that microservices would solve that problem.
To be completely clear:
It is not the fault of the runtime artifact style “monolith” that your code has become unmaintainable.
It is your fault, the fault of the people involved!
A deployment monolith – and that is what we talk about if we talk about “monolith” – does not need to be a big ball of mud. It can be perfectly structured, using the same principles everywhere in the code, easy to understand, easy to maintain, easy to change. All we need for this is modularization, a concept ubiquitous in IT for more than 60 years – and a bit of goodwill and discipline.
It is even possible to have multiple autonomous teams working with a single monolith and to deploy the monolith multiple times per day. In their earlier days, Etsy used a well-modularized monolith. With that approach, they were able to work with multiple teams in the same codebase in a loosely coupled way and to deploy their monolith more than 50 times per day.
Still, most monolithic applications are not that way. They are a big ball of mud, nobody really understands anymore and that have become very hard to maintain and change.
But again: This is not the fault of the runtime artifact style “monolith”. Instead, it has a lot to do with people, processes and organization:
- Missing communication paths: Even in times of agility, we still see that most software development processes are implemented as a one-way road: The business departments write down their requirements (in agile speak: “user stories”) and the software engineers have to implement them. No feedback. No discussions. In the worst case, some people (e.g., requirement engineers, pseudo product owners, business-IT coordinators) sit between the business and the IT department and meticulously make sure, both parties do not talk to each other directly. This way, many requirements are imposed on the software engineers that perfectly mess with the existing design. With a bit of feedback and discussion, most of these cases could be solved easily, but without such a feedback channel, the big ball of mud is already scheduled.
- Feature mania: To reinforce the previous issue, in many organizations business features are the sacred cow. It is all about features. Only business features mean business value. Nobody cares about code quality because: No business value. Instead let us put all money in more features to maximize business value. It should be clear that this is a naive assumption because IT systems have a lifetime and they need to be changed all the time until they are retired – not least because of all those features. If software engineers are incentivized for carelessly putting together some code, just good enough that the new feature somehow works, without taking care of overall code understandability and maintainability, the big ball of mud is already scheduled.
- Lack of documentation: At the software engineering side, we often see a lack of (useful) documentation: What are the underlying design principles? How is the system organized? What goes where? How to communicate between modules? What are the coding standards? And so on. Writing down these essential design and implementation principles would not be a lot of work – 10-20 pages, maybe around 30 pages for a bigger system. It would also be easy to keep them up to date if anything should change because there is not a lot of stuff to be kept up to date. Yet, we typically find tons of (often useless) documentation in the context of enterprise software. But we basically never find this essential documentation, paving the road towards the big ball of mud.
- Lack of training: Even if the required documentation exists, we all know this does not necessarily mean the people who should read it actually do. To make sure the relevant principles and standards regarding your design and implementation are shared by all software engineers involved, you need regular training. Everyone who joins the team needs to go through such a training before starting to change the software. And once in a while, everyone in the team should repeat the training to make sure things do not drift into oblivion. Regular training probably is the best weapon against software deterioration. It also triggers discussions if some of the defaults need to be updated. This way, the people involved are not only all on the same page. It also makes sure, the standards and principles are always up to date. But we rarely see this kind of training, paving the road towards the big ball of mud.
- Lack of knowledge and skill: Oftentimes, people work on the code without the required knowledge and skills. Being able to write some code does not mean you are capable of working on an application that needs to run reliably for the next 20 years or longer. To illustrate the situation: I have some crafting skills, can work with wood, stone and metal. I also know something about electricity and a bit about plumbing. Still, nobody would hire me to build a house because I do not have the right education and training, meaning I lack some relevant knowledge. Personally, I think that is a good thing. Unfortunately, in IT we tend to let anyone who knows a bit about coding work on our business-critical code. That would be me laying bricks without taking care about the blueprint, static, doors, windows, water pipes, electrical lines or anything. To make things worse, many companies offer low salaries (“They are only developers!”), very little further training, outdated and demotivating environments (and then cry “skills shortage” if nobody shows up for their less than mediocre job offering). This way, these companies often end up with people lacking the required knowledge and skills to develop and maintain business-critical software systems that need to run reliably for the next 20 years. Welcome, big ball of mud!
- Lack of discipline: It takes rigor to stick to the standards and principles of a big application to prevent code deterioration. We cannot simply do everything the way we like it best. If everybody would do it, after a few months we would see dozens of ways doing the same thing, rendering the code non-understandable and thus unmaintainable. And sometimes, it is so tempting just to call that function or method inside of the other module, even if it is not part of the module interface. I mean, the IDE shows the method. Why should I not call it? And then, a lot of people simply do things their preferred way. Feature pressure, lack of documentation and training add their share, but in the end it is a lack of discipline. This may sound harsh, but I have seen way too many people throughout my career who happily added thread after thread to the big ball of mud just because it saved them a bit of time or they liked it better that way 1. Once again: Welcome, big ball of mud!
These were some of the reasons and drivers I know that lead to the dreaded big ball of mud. As always, there are more reasons and drivers. But the listed reasons and drivers already draw a very clear picture as all of them have one trait in common:
The big-ball-of-mud drivers have nothing to do with the runtime artifact style.
But they have everything to do with people, processes and organizations.
So, why do companies think that changing their runtime artifact style would solve their big-ball-of-mud issue if they do not also tackle the issues I listed before?
Or as Simon Brown put it:
[…] this begs the question that if you can’t build a well-structured monolith, what makes you think microservices is the answer? 2
If you simply go for microservices without tackling any of the aforementioned drivers, most likely you will end up with a distributed ball of mud – the worst of both worlds. A big-ball-of-mud monolith sucks at development time but is okay at runtime (because a monolith is the simplest possible runtime artifact). With a distributed ball of mud, both development time and runtime suck because now you added the complexity of running distributed applications without solving any of the development time problems.
The story so far
Companies (still) want to break up their monoliths into microservices. If you ask them why they do it, what they expect from this measure, they very often reply they expect to cure the “big ball of mud” issue with microservices, or to improve their time to market, or both.
In this post, I have discussed that simply changing the runtime artifact style from monolith to microservice will not help with the “big ball of mud” issue because the actual problems lie in the organization, the processes and the people, not in the technology – and especially not in the runtime artifact style.
In the next post, we will look at the “time to market” issue. Stay tuned …
A little example for illustration purposes: I once had the situation I found a class in a package where it most definitely did not belong. I went to the developer who created the class and asked him why he put that class into that package. He replied he would not care about packages. He would search classes using the search functionality of the IDE only. Hence, why bothering where to put classes? Therefore, he decided to ignore package structures completely and add classes wherever the IDE puts it (which usually is the package of the class of the active window). He added that other developer may go for a package structure if they like but he would not care. Side note: The company considered him a great developer because he implemented features very quickly. ↩︎