SOFTWARE ARCHITECTURE / MICROSERVICES
When Not to Split a Service
Signals that a boundary is premature—or overdue.
Key Insight: Splitting a service prematurely doesn't just fail to deliver the benefits of microservices — it actively creates a worse problem than staying a monolith would have: distributed coupling, which is more expensive to fix than the in-process coupling it replaced.
Most architecture advice focuses on when and how to split a service. The less discussed, equally important question is when not to — and getting this wrong in the direction of splitting too early is common, expensive, and hard to reverse, because merging services back together is organizationally and technically harder than most teams expect going in.
Signal: the "services" always change together
If two services can't be deployed independently in practice — a feature routinely requires coordinated changes and coordinated deployment across both — the split hasn't actually created independence, it's just moved a monolith's internal coupling across a network boundary and added latency and operational overhead to it. This is the single clearest signal a split was premature: track how often changes to one service require simultaneous changes to another, and if the answer is "almost always," the boundary is in the wrong place, or it's too early for there to be a boundary at all.
Signal: nobody can explain the boundary in one sentence
A well-chosen service boundary maps to a real business capability that has its own reason to exist and change independently — Billing, Inventory, Notifications. A boundary that exists because "this felt like a separate concern" or "this team wanted their own deployment pipeline" without a clear business justification is a boundary drawn for organizational convenience rather than architectural need, and it tends to require constant renegotiation as the actual usage patterns reveal themselves.
Signal: the team is small enough that coordination isn't actually hard yet
Microservices solve a coordination problem — letting independent teams move independently without stepping on each other. A team of four or five engineers working on one product doesn't have a coordination problem that splitting into services solves; they have a coordination problem that a well-organized modular codebase and good communication solves more cheaply. The operational cost of running multiple services — separate deployments, separate monitoring, network calls where function calls used to be — is a cost paid regardless of team size, and it only starts paying for itself once the coordination problem it solves is actually present.
Signal: you'd be guessing at the boundary
Splitting a system before you deeply understand its actual usage patterns and natural seams means guessing where the boundary should be, based on assumptions rather than evidence. A modular monolith, where boundaries exist as enforced code-level modules rather than network-level services, gives you almost all the organizational clarity with a much cheaper way to discover you guessed wrong — moving code between modules in one codebase is a refactor; moving functionality between two live, deployed services with their own data stores and consumers is a migration project.
Building the modular monolith first, and extracting a service once a specific module has proven, through real usage, that it needs independent scaling or independent deployment, replaces a guess with evidence.
The overdue side: when staying merged is the actual mistake
None of this argues against ever splitting a service — it argues for splitting for real, observed reasons. The signals that a split is genuinely overdue: one module's load is high enough that scaling the entire monolith to serve it is wasteful; one team's deployment cadence is being blocked by unrelated changes from another team working in the same deployable; one module's reliability requirements are different enough from the rest (it needs to stay up during a deployment that would otherwise require the whole system to restart) that shared deployment is actively costing you something measurable.
When those pressures are real and present, the split is solving an actual problem, and the modular structure you built first makes the extraction far more mechanical than if you'd started with a poorly-guessed boundary drawn before any of these pressures existed.
Key takeaways
If two services almost always change and deploy together, the split isn't providing independence — it's added overhead on top of the same coupling. A boundary you can't justify in one sentence around a real business capability is likely organizational convenience, not architecture. A small team without a real coordination problem doesn't need the operational cost of multiple services yet. Build boundaries as enforced modules first and extract to services once real usage evidence — not a guess — justifies it. Split when scaling, deployment cadence, or reliability needs genuinely diverge, and let those be the reasons, not the default.
