The microservices debate is usually conducted as a technical argument and is almost always an organisational one. The question is not which architecture is better. It is whether your bottleneck is the system or the number of people trying to change it at once.
What each actually optimises for
A monolith optimises for simplicity. One deployable, one database, one place to look. Calls between modules are function calls — fast, reliable, transactional. Local development is running one thing.
Microservices optimise for independent deployment. Teams ship on their own schedule without coordinating releases, services scale separately, and a failure can be contained. The price is that every in-process call becomes a network call, with everything that implies.
Notice that the microservices benefit is mostly about teams. With one team of six, independent deployment solves a problem you do not have.
The costs that get underestimated
- Distributed debugging. A request crossing eight services needs distributed tracing to diagnose at all. Without it, incidents take hours instead of minutes.
- Data consistency. Transactions across services require sagas, compensating actions and eventual consistency. This is genuinely hard and is where most of the serious bugs live.
- Operational surface. Each service needs deployment, monitoring, alerting, on-call, dependency updates and security patching. Twenty services is twenty of everything.
- Local development. Running the full system on a laptop stops being possible, and the workarounds — shared dev environments, extensive mocking — all have their own costs.
- Latency. A chain of network calls adds up. A request that was two milliseconds becomes forty.
None of these are arguments against microservices. They are the bill, and it should be read before signing.
The decision questions
- How many engineers are working on this system? Below about fifteen, a monolith is almost always right. Above fifty, coordination costs usually justify splitting. Between the two, it depends on everything else.
- Are deployments actually blocked on each other? If teams routinely wait to release, that is the organisational pain microservices address. If releases are smooth, the problem is elsewhere.
- Do parts of the system have genuinely different scaling profiles? A video transcoder and a settings page have different needs. Most CRUD applications do not.
- Can you run the operational overhead? Do you have CI/CD, centralised logging, tracing, service discovery and a plausible on-call rotation? If not, these come first and are a prerequisite, not a follow-up.
- Do you understand the domain boundaries? Splitting along the wrong seams produces a distributed monolith — all the operational cost with none of the independence. Systems whose domain is still being discovered should stay together.
The modular monolith
For most teams the right answer is neither extreme. A modular monolith is a single deployable with strictly enforced internal boundaries: clearly defined modules, communication only through published interfaces, and no reaching into another module’s tables.
This gives most of the organisational benefit — teams own modules, boundaries are clear, the code is comprehensible — without the operational cost. It also makes later extraction straightforward, because the seams already exist.
Enforcement is the hard part, and it needs to be mechanical rather than cultural: build-time checks on package dependencies, separate schemas with no cross-schema queries, architecture tests in CI. Boundaries maintained only by code review erode within a year.
How to split, when you do
Extraction works far better than greenfield decomposition.
Start by identifying a module with clear boundaries, few dependencies and a reason to be separate — different scaling, different compliance requirements, a team that would genuinely benefit from independence.
Extract one service and run it in production for a quarter. You will learn what your real operational gaps are, and the lesson is much cheaper at one service than at fifteen.
Separate the data as part of the extraction, not afterwards. A service sharing a database with the monolith is not independent; it is a deployment unit with a hidden coupling, and the coupling will surface at the worst moment.
Then stop and reassess. Many organisations find that extracting two or three services solves the actual problem, and the remaining monolith is perfectly serviceable.
Signs you split too early
Four symptoms, all common.
Changes routinely require touching several services. The boundaries are in the wrong place; you have distributed a module, not decomposed a domain.
Services are deployed together. If they must release in lockstep, they are one service with extra network hops.
Shared libraries are the integration mechanism. A common library that every service must upgrade together recreates the coordination problem the split was meant to solve.
Nobody can run the system locally. This slows every developer every day, and it is the cost most often dismissed as temporary.
What to build before splitting
Three capabilities are prerequisites rather than nice-to-haves.
Automated deployment per service. Manual deployment of fifteen services is untenable. See CI/CD best practices.
Distributed tracing. Not just logs. You need to follow one request across service boundaries, which is the subject of observability versus monitoring.
Infrastructure as code. Provisioning each new service by hand does not scale past about three. See infrastructure as code.
A worked scenario
A company with twelve engineers runs a monolith. Releases happen weekly and are painful: a bug in any area blocks the whole release, and the Thursday deployment regularly slips to Friday.
The instinct is to split into services. The actual diagnosis is worth doing first. If releases are painful because the test suite takes ninety minutes and is flaky, microservices will not help — the same tests will run, in more places. If they are painful because four teams must agree the release is ready, that is a coordination problem and splitting does address it.
In practice the first problem is far more common at twelve engineers. Fixing the pipeline — faster tests, trunk-based development, feature flags, deploy on merge — often removes the pain entirely and costs a fraction of a decomposition. See CI/CD best practices.
At fifty engineers the calculus changes, because no amount of pipeline work removes the need for five teams to coordinate a single deployable.
Organisational shape and system shape
Systems tend to mirror the communication structure of the organisation that builds them. This cuts both ways, and it is the most practical lens available for this decision.
If you have three teams with clear, stable ownership of three business areas, three services will probably work well, because the boundaries already exist socially. If ownership is fluid and everyone works on everything, service boundaries will be crossed constantly and will feel like obstruction rather than structure.
The corollary is that changing the architecture without changing the organisation rarely sticks. If you want services owned independently, teams must be able to deliver independently — which means owning their own deployment, their own on-call and their own roadmap.
The decision is reversible in one direction
Extracting a service from a monolith is a known, bounded piece of work. Merging fifteen services back into one is a rewrite. That asymmetry argues for staying together longer than feels comfortable and splitting deliberately when the evidence is clear.
It also argues for keeping the monolith in good condition rather than letting it decay into something people want to escape. A great deal of the enthusiasm for decomposition is really enthusiasm for leaving a codebase nobody has maintained, and managing that debt is considerably cheaper than distributing it.
Frequently asked questions
Can a monolith scale?
Yes. Horizontally scaled monoliths serve very large workloads routinely. Scaling limits are usually in the database, and that constraint does not disappear by splitting the application layer.
Is serverless an alternative?
It is a deployment model rather than an architecture. You can run a monolith or microservices on serverless. It reduces some operational burden and introduces its own constraints around cold starts, execution limits and local testing.
How many microservices is too many?
A rough heuristic is no more services than you have engineers, and ideally each service owned by a team rather than an individual. Services with no clear owner become nobody’s problem until they break.
What is a distributed monolith?
Separate services that must be deployed together because they are tightly coupled. It combines the operational cost of microservices with the coordination cost of a monolith, and it is the most common failure mode of a rushed split.
