The question is organisational before it is technical

Microservices are a solution to a coordination problem. When one deployable is owned by forty engineers across six teams, every release becomes a negotiation and the slowest team sets the pace for everyone. Splitting the deployable lets teams ship independently. That is the actual benefit, and it is organisational.

Which means the first question is not about technology, it is how many teams need to deploy independently. With one team of eight the answer is almost always a monolith, because you would be paying distributed-systems costs to solve a coordination problem you do not have. Conway's law is not a warning here — it is the design input.

The four questions I actually ask

How many teams must deploy without coordinating? How different are the scaling profiles of your components — does one part need ten times the capacity of the rest? Do parts have genuinely different availability requirements, where one must stay up while another may go down? And are the domain boundaries clear enough today that you could draw them on a whiteboard without an argument?

If the answers are one team, uniform scaling, uniform availability and unclear boundaries, build a monolith and stop reading. If they are many teams, sharply divergent scaling, differing criticality and boundaries everyone already agrees on, services are justified. Most real situations land in between, which is where the modular monolith belongs.

The modular monolith is the underrated answer

A single deployable with strictly enforced internal module boundaries gets you most of the architectural benefit and almost none of the operational cost. Modules communicate through defined interfaces, do not reach into each other's tables, and could in principle be extracted. Meanwhile you keep one deployment, one database transaction, and a stack trace that spans the whole request.

The discipline has to be enforced mechanically or it evaporates. Package structure, dependency rules checked in CI, and separate schemas per module make the boundaries real. Done properly, extraction later is genuinely straightforward — and often you discover you never needed to, which is a success rather than a failure.

The costs teams consistently underestimate

Local development is the first surprise. Running one service is easy; running fourteen to reproduce a bug is a productivity tax paid every day by every engineer. Then debugging: without distributed tracing a request spanning six services has no coherent trace, and adding tracing later is far more painful than starting with it.

Data is where it really bites. A transaction that was atomic in a monolith becomes a distributed workflow needing sagas and compensating actions. Reporting that was one join becomes an aggregation across services that share no database. I have watched teams rebuild half a platform because service boundaries were drawn without considering which queries would need to cross them.

When microservices are actively wrong

A startup before product-market fit should not be splitting services, because the boundaries will move as the product changes and every move is now a cross-service migration. Distributed systems make change expensive, which is precisely the wrong trade when your main activity is changing things.

Small teams with a large service count is the other clear failure. Six services owned by four engineers means everyone context-switches across multiple codebases, on-call spans everything, and nobody has depth anywhere. If your service count exceeds your team count, you have almost certainly split too far.

Extract along the seams that hurt

When extraction is justified, do it one service at a time, starting with the component that has the clearest boundary and the most distinct scaling or availability need. Not the one that is most annoying to work in — annoying usually means messy, and extracting a messy component distributes the mess rather than fixing it.

Clean up inside the monolith first, then extract. And extract the component, its data and its ownership together. A service still reading the monolith's tables is not a service; it is a monolith with extra network calls and worse failure modes, carrying all of the costs and none of the independence.

Key takeaways

  • The primary driver is how many teams must deploy independently — an organisational question, not a technical one
  • Ask four questions: team count, scaling divergence, availability divergence, boundary clarity
  • The modular monolith gives most of the benefit at almost none of the cost, if boundaries are enforced in CI
  • Underestimated costs: local development, distributed tracing, and turning atomic transactions into sagas
  • Pre-product-market-fit startups and teams with more services than engineers should not split
  • Extract one clean, well-bounded component at a time — with its data and ownership, or it is not really a service

Conclusion

There is no default answer, and 'start with a monolith and split later' is too glib because splitting later is expensive and often never happens. Answer the four questions honestly, choose the modular monolith when unsure, and let real pain rather than fashion drive extraction.

Enjoyed this article?

Vivek Kumar Singh

Vivek Kumar Singh

Technical Expert · Full Stack Cloud Engineer · Tokyo, Japan