Monolith vs Services
Date: 2026-08-17
Whether the system deploys as one unit or many. The tradeoff is presented as technical and is almost entirely organisational — services buy independent deployment for teams that need it, and charge a distributed system for it whether they need one or not.
A monolith is a system built and deployed as a single unit; a services architecture splits it into separately deployed units that communicate over the network.
The distinction that matters
Not code layout. Deployment coupling.
MONOLITH SERVICES
one build many builds
one deploy many deploys, independently
one runtime many runtimes
function calls between modules network calls between services
one database, transactions work many databases, transactions don't
A codebase split into tidy modules that still ship together is a monolith, and that’s fine — it’s usually the right shape. A repository per service that can only be released in lockstep is a distributed monolith: every cost of services, none of the benefit, and the worst outcome available.
What services actually buy
- Independent deploy. Team A ships without coordinating with teams B through F. This is the real benefit and everything else is secondary
- Independent scaling. The search service scales on Black Friday; the admin panel doesn’t
- Fault isolation — if designed for. A service that goes down without taking others with it requires deliberate work, not just separation — Graceful Degradation
- Technology choice per service. Genuine occasionally, usually an excuse
The threshold is team count, not codebase size. One team ships a monolith faster at any size. Six teams contending for one deploy pipeline spend more time coordinating than building — that’s the pain services solve, and below it they solve nothing.
Conway’s law is the underlying observation: a system’s structure tends to mirror the communication structure of the organisation that built it. Service boundaries drawn against team boundaries get eroded back to matching them.
What they cost
| Free in a monolith | Work in services |
|---|---|
| A function call | A network call that can fail, retry and time out |
| A database transaction | Sagas, compensating actions, Eventual Consistency |
| A stack trace | Distributed tracing — Observability |
| Refactoring across a boundary | A versioned API change with a deprecation window |
| Running it locally | Containers, stubs, or a shared environment — Environments |
| ”Where did this data come from” | An investigation |
The costs are all the same cost: a network appeared between two pieces of your program, and networks are unreliable, slow and partially available.
Choosing a boundary
If you split, split on these — in order of how well they hold:
- Team ownership. Can one team own this end to end?
- Rate of change. Things that change together belong together — Coupling and Cohesion
- Data ownership. One service writes a given table. Two services writing one database is the boundary already failing
- Different scaling or availability needs. Genuine and easy to test for
Bad boundaries, all common: by technical layer (a “database service”), by entity noun without regard to who changes it, by what’s easy to extract rather than what’s coupled.
The sequence that works
Monolith first, extract under pressure. Boundaries are guesses at the start of a project and evidence after a year of change — extracting later is easy, un-extracting is not.
1 modular monolith clear internal boundaries, one deploy
2 identify the pain one module blocks releases, or needs
separate scaling, or has its own team
3 extract that one via The Strangler Pattern — route traffic away
piece by piece, never a rewrite
4 stop extract the next only when it hurts too
Step 3 is the one with a method attached — extraction happens by routing traffic away from the module piece by piece, never by rewriting it alongside — The Strangler Pattern.
Most systems should stop at step 1. A well-partitioned monolith with a good CI pipeline outperforms six services for the overwhelming majority of commerce sites, and “modular monolith” is a real destination rather than a stage.
Where it interacts
- Backend for Frontend — the pattern that stops a service split leaking into every client
- Event-Driven Architecture — how services avoid calling each other synchronously and inheriting each other’s downtime
- Service Level Objectives — with services, one team’s reliability becomes another team’s dependency, so it needs stating numerically
- CAP Theorem — the constraint that makes cross-service consistency a design problem rather than a database feature