Microservices vs. Monolith: What Actually Justifies the Split
Most "should we move to microservices" conversations start from the wrong premise: that the monolith itself is the problem. Usually it isn't. The problem is that three teams can't deploy independently, or that one noisy code path takes the whole system down with it, and a service split is one possible fix among several, not the automatic answer.
Getting this decision wrong is expensive in both directions. Splitting too early trades a code organization problem for a distributed systems problem, and staying monolithic too long lets deployment coupling quietly cap how fast every team can ship.
The Question to Ask Instead of "Microservices or Monolith"
Ask what's actually being coupled: is it the code, the deployment, or the failure domain? Messy code inside a single service is a refactoring problem, and a service split doesn't fix it, it just moves the mess across a network boundary where it's harder to trace.
A deployment coupling problem looks different: one team's change can't ship without another team's sign off, because their code lives in the same build and release pipeline. That's the pain a service boundary genuinely solves, by giving each team its own deploy pipeline for the piece it owns.
When a Monolith Is Still the Right Answer
If one team owns the whole codebase, or if two or three teams rarely touch the same modules, a monolith with clean internal boundaries usually outperforms a service split on every axis that matters: fewer moving parts to operate, simpler debugging, and no network calls standing in for what used to be a function call.
A monolith also keeps transactions simple. A single database transaction that used to update three related tables becomes a coordination problem across three services once you split them, and that coordination problem is real engineering cost you're taking on, not a side effect you can ignore.
The Signal That Actually Justifies Splitting a Service Out
The strongest signal is a recurring, specific pain: a component with wildly different scaling needs than the rest of the system, or a component whose failures keep taking down unrelated features because they share a process and a deploy. If your recommendation engine needs ten times the compute of everything else, or a flaky third-party integration keeps crashing the whole app instead of just itself, that's a concrete case for isolating it.
A vague sense that "the codebase is getting big" isn't that signal. Size alone is a refactoring problem you can usually solve with better internal module boundaries, without touching your deployment topology at all.
Splitting a service out is justified by signals like these:
- A component has wildly different scaling needs than the rest of the system.
- A component's failures repeatedly take down unrelated parts of the system with them.
- Several teams cannot deploy independently because they are coupled through the same deployment.
- The pain is recurring and specific, not a general feeling that the codebase is messy.
Where Teams Get Burned Splitting Too Early
The cost that catches teams off guard isn't writing the second service, it's everything that comes with running two services instead of one: distributed tracing to follow a request across a network hop, retry and timeout logic for calls that used to be reliable function calls, and a data consistency story for anything that used to be one database transaction.
Teams that split early usually end up with a distributed monolith: two or more services that still deploy together because they're too tightly coupled to change independently, but now pay the network and operational tax of being separate processes. That's the worst of both models at once, and it's a common enough outcome to plan around rather than assume you'll avoid it.
A Middle Path: Enforce Boundaries Before You Physically Split
Before extracting a service, enforce the same boundary inside the monolith first: a module with a defined interface that the rest of the codebase can only call through, not one that other modules reach into directly for a shortcut. If a team can maintain that discipline inside a single deployable, splitting it out later is mostly an infrastructure change, not a redesign.
If a team can't maintain that discipline inside a monolith, splitting the module into its own service usually doesn't fix the underlying problem either, it just makes the boundary violation a network call and a production incident instead of a code review comment. Fix the discipline first; the physical split is the easy part once the boundary is real.
What Good Looks Like
Good architectural boundary decisions are driven by a specific, named pain (deployment coupling or failure isolation), with the boundary enforced inside the codebase before it's ever turned into a separate deployable service.
Building The Capability (5-Stage Skill Ladder)
How to Get Started
Frequently Asked Questions
How many services is too many for a five person engineering team?
There's no fixed number, but watch for the symptom rather than counting services: if engineers spend more time coordinating deploys and chasing a request across services than writing features, you've likely split further than your team size can operate well. Consolidating two rarely-changed services back together is a legitimate move, not a failure.
Do we need Kubernetes to run more than one service?
No. A handful of services can run on simpler infrastructure, a couple of managed containers or a platform as a service, without the operational overhead of running an orchestrator yourselves. Kubernetes earns its complexity at a scale of services and traffic most small teams haven't reached yet.
What's usually the first service worth splitting out of a monolith?
Whichever component has the clearest, most painful scaling or failure isolation need right now, not the one that's theoretically the cleanest boundary on a whiteboard. A background job processor or a single flaky external integration is a common first candidate, since isolating it has an immediate, measurable benefit.
About the numbers
This guide doesn't quote a sourced benchmark. Figures in it are estimates or general guidance, so check them against your own numbers.
Related Guides
Monolith or Microservices: How to Tell Which One You Actually Need
How to decide between a monolith and microservices based on your team size and deploy needs, not on which one sounds more modern.
How to Decide Where Your Next Service Boundary Actually Belongs
A decision guide for CTOs choosing whether to split a piece of a monolith into its own service, built around four concrete criteria, not team size.
When Splitting a Pipeline Into Services Is Worth the Cost
A tradeoff comparison for when decomposing a monolithic data pipeline into separate services actually pays off, and when it just adds coordination cost.
Where Production Deployment Budgets Quietly Leak
The recurring places engineering teams overspend on production deployment architecture, and a practical order for fixing them without a full rebuild.
How to Tell If Your Monolith Actually Needs Splitting
A way to decide which parts of a monolith, if any, actually need a hard service boundary, instead of splitting everything or staying stuck out of habit.
Modular Monolith or Microservices: A Decision Guide
How to decide between a modular monolith and microservices based on your team size and deploy pain, not which architecture sounds more serious.