Somewhere there is a team that split their application into a dozen services for the right reason at the right time. Most teams did it for a wrong one: "microservices are the modern architecture". This article is the case for the opposite default — one service, one repository of truth, split only when the seam is real and the measure is data.
What the architecture actually negotiates
Every architecture choice is a negotiation about what breaks when. A monolith makes three commitments:
- One deployable — every change ships together. Deployment coordination cost is close to zero; incident coordination is implied.
- One runtime graph — function calls stay calls. No serialization, no partial failures, no network timeouts between your own modules.
- One owner culture — a deploy that breaks the whole thing is everyone's problem, which is exactly the incentive structure you want in early years.
Services trade those for three different things:
- Independent deploys — a team can ship without asking anyone's permission.
- Independent failure domains — an errant component degrades part of the system.
- Independent scaling — one component at 100× load doesn't drag the rest.
Notice the trade is asymmetric: the costs of services (networks, serialization, version skew, distributed tracing, schema migrations across teams, rollback across services) are incurred immediately and permanently, while the benefits only materialize at specific scale/latency situations most applications never reach.
The numbers most architecture discussions never quote
The cost category chart people don't draw, because it's inconvenient:
operator monolith (1 deployable) services (n deployables)
deploys/week ~1, atomic ~n · n-1 coordination edges
rollback instant, one unit orchestrating n services' history
request path in-process call actual TCP+TLS+queue round trips
failure mode one blast radius n² partial states to reason about
debugging distance stack trace only traces, logs, dumps across services
perf behavior cache-friendly serialization-dominatedEvery row after line 1 is a physics difference, paid in milliseconds and incident minutes. The "it's basically the same code" framing of splitting is fiction: a monolith's p99 includes one process hop; a services' p99 includes the network, per hop, forever.
The three real reasons to split
The honest playbook — the one that survives contact with production — is: split only when a measured, specific constraint says so. The three constraints that actually justify it:
1. Independent scaling with divergent curves. One component grows 100× while siblings stay flat, and separate capacity management genuinely pays. (Autoscaling by CPU per service is the version you can measure.)
2. Independent failure domains. One component is allowed to be down part of the time — because a queue can tank, or a webhook can go to a flaky partner — without taking the rest of the product down. (The failure is already there in the monolith; services give it a boundary.)
3. Team-scale coordination. When independent deploys actually free two teams from a release train — meaning deploy permissions, ownership, and on-call align with the split — not when a diagram prettier is the motivation.
What splitting should feel like
A healthy split is a seam extraction, done in the worst-pain order, one at a time:
1. find the module that outgrew its owners (a team owns it; the rewrite is scheduled)
2. extract it as a *library first* — same code, different boundary within the monolith
3. then a service, behind a narrow interface
4. then let the new service's p99/inch app prove the move before anything else followsThe tell-tale sign of the wrong order: the team that split two services before drawing a single endpoint contract, and whose "architecture" now lives in a wiki that no code enforces — the strongest signal the whole exercise was ceremony.
When the page is finally honest
The famous retrospective (Graham, Lean Production blog — "microservices are a bet") earned its fame by saying the quiet part: 95% of companies should not implement microservices, and Amazon/Scalability folklore was a bad source of advice — because Amazon's scale is not a configuration you can copy; it's a constraint that emerged from measured demand.
None of this is "never". It is: default to one deployable; make the seam first; extract one component at a time; and require a measured, specific constraint — not a trendline — as the price of admission.