When to get rid of your monoliths and when not
Microservices is a hot topic. There is a plethora of opinions, conference talks and blog posts about microservices. I started doing microservices myself three-and-a-half years ago, when I was designing a system for an insurance company. Since then I have done many talks on the topic, published articles and presented training courses.
Usually, I ask my attendees if they have a big monolithical system that they would love to get rid of. In most cases, they have. Of course. They wouldn’t come to my talks or invite me to do courses if they hadn’t. And there’s good reason you want to abandon your monoliths. Let me give you some examples. Real life examples.
One of my clients had ran most of its production software on a mainframe. The code was written in an interesting COBOL dialect. There’s 18 million lines of code. Keeping the mainframe alive alone costed over 1 million euros per year. Not to mention that most of the COBOL programmers are on the brink of retirement.
Another company I’ve worked with started out with two guys, of which one wrote code. In VBA. When the company started growing, they migrated to Visual Basic. Currently, the company employs over 20 developers, and writes code both in C# and in VB.NET. The product has grown to almost 4 million lines of code.
One last example. Over the course of 12 years, a third company has written a 3 million lines of code application. In PHP. The complexity of the database and the many, many queries in code on top of it make it hard to add new features.
Different companies, different scenario’s. What struck me however, is the number of striking resemblances in these scenario’s:
- In all three companies, people worked on these systems for many years. Different people that is. With different insights, different architectural ideas, and different coding styles.
- As a consequence, over time the architecture blurred and became increasingly harder to maintain or extend.
- There are no automated (unit or integration) tests in any of these scenario’s. With COBOL I can understand that. With more modern languages such as Java, C#, VB.NETor PHP, there is no excuse.
- In all three scenarios adding new features became virtually impossible. As one the testers explained to me: “With every bug we solve, we introduce a new one.”
Need for speed
So where did it go wrong for these companies? The clear commonalities in these scenarios allowed for the architecture to fail. These companies let their software grow over quite long periods of time. Twenty years. Twelve years. And continuously, they had to choose between adding new features or maintaining quality. If you want to keep your code maintainable over time, you will need to find a good balance between features and quality.
There’s an old metaphor that mimics the behavior of such companies. The boiling frog syndrome. If a frog is put into boiling water suddenly, it will jump out. However, if it is put in tepid water which is then brought to a boil slowly, it will not notice the danger and it will be cooked to death. Although the story is nonsense, as a metaphor it is quite accurate.
Companies always seem to need to grow. There’s always a need for speed. Business always comes up with more features that the teams are able to add to the systems. As a result, companies often choose for the short-term benefits rather than the long-term. New features over maintainability. “We’ll add tests later,” or “we’ll leave some TODO’s in the code that we fix later.”
The thing is: your code doesn’t break when you first build it. It breaks ten or fifteen years later. And in places that have never thought of.
This is the state that I usually find organizations in. Boiling frogs that ask me to help them get rid about their unmaintainable monoliths. The architecture is a mess. There are no automated tests. The system doesn’t scale well. And with every code change the system falls apart somewhere else.
Since the phrase was coined almost four years ago, may companies now believe that microservices will solve their problems. Microservices bring goodness. Small and indepedently deployable components that help you scale and write easy maintainable and testable code.
Yet, implementing microservices also comes with a lot of questions that you need to answer along the way, such as:
- What makes a component a microservice?
- How do I integrate all these small, independently deployable components?
- How do I design the interfaces of these components, the API’s?
- What does being RESTful mean?
- How do I design the implementation of my components?
- How do I arrange for authorization and authentication?
- Which technologies do I use to build, test and deploy my microservices?
- How do I set up and maintain automated deployment pipelines?
- Can I integrate my microservices with other, more traditional systems in our landscape?
- And last but not least. Microservices imply that your system is a distributed system. So how do I arrange integrity and consistency of my data if one the services is temporarily unavailable?
All in all, microservices are an interesting architectural choice if you need to discontinue your monoliths. But microservices do come with some serious consequences. Finding the right answers to the questions above, specific to your company, can be quite an challenge. Please take moving towards microservice seriously. It takes time, effort and preferably experience. If you haven’t been able to keep your architecture, code and tests in order for the past 15 years, do you think you are able to implement microservices, which is an even harder architecture?
Do you really need microservices now?
I’m quite convinced microservices are the right architecture for this highly scalable era. Microservices have many benefits. They improve your scalability. They allow you to deploy individual small and replaceable parts throughout your landscape. But microservices also add one more layer of complexity to your landscape. Think about RESTful API design. Think about authorization and authentication. New (cloud) infrastructures. Or the fact that you are building distributed systems now.
Yet, the majority of boiling frogs lack time and experience for such large-scale migrations of their products. They don’t have time to refactor. New features need to be added! These boiling frogs should investigate if they need to migrate to microservices now.
There are plenty aspects of microservices that on their own already provide a lot of improvements, without having to go the full Monty:
- Adhere to a more agile approach.
- Improve the collaboration in teams and include both business and operations.
- Improve the modular design of your monoliths.
- Investigate the patterns from domain driven design, such as bounded contexts and aggregates.
- Increase automated testing, so refactoring to more modular design is less hard.
- Set-up automated deployment pipelines. These allow you to move to more regular deployments, thus removing all kinds of manual deployment activities.
Maybe your main challenge is to improve the quality and maintainability of your code. Or shortening your time to market. Or perhaps innovating faster. In these cases the techniques above will already help you get there, without having to move directly to microservices all at once.
Bottom line? Are you thinking about moving towards newer architectures? Microservices? Or even serverless? Always start by answering the question what you really need to improve. And only after having answered that question improve step-by-step. Please don’t become the next generation of boiling frogs.