Give Me Back My Monolith
It feels like we’re starting to pass the peak of the hype cycle of microservices. It’s no longer multiple times a week we now see a blog post of “How I migrated my monolith to 150 services”. Now I often hear a bit more of the counter: “I don’t hate my monolith, I just care that things stay performant”. We’ve actually seen some migrations from micro-services back to a monolith. When you go from one large application to multiple smaller services there are a number of new things you have to tackle, here is a rundown of all the things that were simple that you now get to re-visit:
Setup went from intro chem to quantum mechanics
Setting up a basic database and my application with a background process was a pretty defined process. I’d have the readme on Github, and often in an hour or maybe a few I’d be up and running when I started on a new project. Onboarding a new engineering, at least for an initial environment would be done in the first day. As we ventured into micro-services onboarding time skyrocketed. Yes, we have docker and orchestration such as K8s these days to help, but the time from start to up and running a K8s cluster just to onboard a new engineer is orders of magnitude larger than we saw a few years ago. For many junior engineers this is a burden that really is unnecessary complexity.
So long for understanding our systems
Lets stay on the junior engineer perspective for just a moment. Back when we had monolithic apps if you had an error you had a clear stacktrace to see where it originated from and could jump right in and debug. Now we have a service that talks to another service, that queues something on a message bus, that another service processes, and then we have an error. We have to piece together all of these pieces to eventually learn that service a was on version 11 and service q was expecting vesion 12 already. This in contrast to my standard consolidated log, and lets not forget my interactive terminal/debugger for when I wanted to go step by step through the process. Debugging and understanding is now inherintly more complicated.
If we can’t debug them, maybe we can test them
Continuous integration and continuous development is now starting to become common place. Most new apps I see now days automatically build and run their tests with a new PR and require tests to pass and review before check-in. These are great processes to have in place and have been a big shift for a lot of companies. But now to really test my service I have to bring up a complete working version of my application. Remember back to onboarding that new engineer with their 150 service K8s cluster? Well now we get to teach our CI system how to bring up all those systems to actually test that things are working. That is probably a bit too much effort so we’re just going to test each piece in isolation, I’m sure our specs were good enough that APIs are clean and service failure is isolated and won’t impact others.
All the trade-offs are for a good reason. Right?
There are a lot of reasons to migrate to micro-services. I’ve heard cases for more agility, for scaling your teams, for performance, to give you a more resilient service. The reality we’ve invested decades into development practices and tooling around monoliths that are still maturing. In my day to day I work with a lot of folks from all different stacks. Usually we’re talking about scaling because they’re running into limits of a single node Postgres database. Most of our conversation focuses on scaling the database.
But in all the conversations I’m fascinated to learn about their architecture. Where are they in their journey to micro-services. It has been an interesting trend to see more and more reactions “We’re happy with our monolithic app.” The road to micro-services may work fine for lots, and the benefits may outweigh the bumpy road to get there, but personally give me my monolithic app and a beach somewhere and I’ll be happy.