One monolith block beside scattered connected services

I have a confession. Around 2016 I was the guy in meetings saying we should break everything into microservices. It was the modern thing, the conference talks were beautiful, and the monolith felt like the past. Some years later, with the scars to prove it, I changed my mind. Not against microservices, I work with them today and they earn their place in the right context. Against microservices as a default answer.

The post that started my change was Martin Fowler’s MonolithFirst. His observation is simple and uncomfortable. Almost every successful microservices system he saw started as a monolith that was split later. And almost every system built as microservices from day one ended in trouble. The reason is that good service boundaries come from understanding the domain, and you only understand the domain after living with the system for a while. Guess the boundaries on day one and you will guess wrong, and a wrong boundary between two services is much more expensive to fix than a wrong boundary between two modules.

Sam Newman, who literally wrote the book on microservices, says a similar thing. Microservices are an outcome you pay for to get specific benefits, mostly independent deployment and independent scaling of teams. If you do not need those benefits yet, you are paying the cost and getting nothing back.

The microservices tax is real money

Let me talk about that cost, because in 2016 nobody put it on a spreadsheet and that was the mistake.

When you split a monolith into ten services, you do not have one system anymore, you have a distributed system. That means service discovery, retries, timeouts, message queues, and data that is eventually consistent whether you like it or not. Each of those is an infra bill and an engineering bill.

Observability stops being optional. With one service, a stack trace tells you what happened. With ten, you need distributed tracing, centralized logs, dashboards per service, and someone who keeps all of that working. The tools cost real dollars per month. The people cost more.

And then on call. Ten services means ten things that can wake someone at 3am, and the failure modes are weirder because they live in the network between services. Tired engineers make worse decisions and eventually they quit. The on call cost is the one that never shows up in architecture diagrams but it shows up in your retention numbers.

I have seen a rough pattern over the years. A small team that goes early to microservices spends something like a quarter of its capacity just feeding the architecture. Pipelines, infra upgrades, debugging cross service issues, keeping contracts in sync. That is one developer out of four working for the architecture instead of the customer. At a fully loaded cost of well over a hundred thousand dollars per year per developer, that is the price of a default decision someone made in a meeting in twenty minutes.

The questions I ask now

When someone proposes microservices today, I do not argue about technology. I ask three boring questions.

How many teams do you have? Not developers, teams. Microservices solve a people problem, too many people stepping on each other in one codebase. If you have one team of five, you do not have that problem. A well organized monolith with clear modules gives you the same separation without the network in the middle.

How often do you need to deploy parts independently? If the whole product ships together anyway, and for most small products it does, independent deployment is a benefit you cannot even use.

Can you afford the tax? Honestly count the infra, the observability tooling, the on call load. If the answer makes you uncomfortable, that is your answer.

If the answers are many teams, frequent independent releases, and yes we can pay, then great, microservices are the right tool. That is exactly the scale where they shine. Below that, you are buying an enterprise solution for a startup problem.

Boring is a feature

A modular monolith, where the modules have clear boundaries inside one deployable, gives you most of the design benefits at a fraction of the cost. And here is the nice part, if one module later proves it needs to scale alone, the clear boundary makes extraction cheap. You kept the option open without paying for it upfront.

Living in Canada taught me something about winter driving that applies here. You do not buy the biggest truck because trucks look strong. You buy what matches the road you actually drive. Architecture is the same. Match the system to the team you have and the road you are on today, and keep the money for when the road actually changes.

So no, I do not say microservices for everything anymore. I say show me the team size, the deploy needs, and the budget. The architecture that wins is the one the business can afford to run, not the one that looks best on a slide.

Pax et bonum.