The proposal arrives with confidence: delivery is slow, the codebase is large, and the answer is to break it into services. Eighteen months later delivery is slower, the same coupling exists across a network boundary, and debugging now requires correlating logs from six systems.
The architecture was not the constraint. It rarely is.
Diagnose the actual delay
Before restructuring anything, measure where a change spends its life. Time from first commit to merged. Time from merged to deployed. Time waiting for review, for a test run, for a release window, for another team.
In most organisations the dominant cost is queueing, not compilation. A forty-minute test suite and a weekly release train will defeat any architecture. Neither is fixed by splitting the codebase, and both are considerably cheaper to fix.
What services genuinely solve
Independent deployment when teams genuinely need to release on different cadences. Independent scaling when one component's resource profile is dramatically different from the rest. Fault isolation when one subsystem must not be able to take down another. Technology divergence when a component has a genuinely different requirement.
These are real problems and services are a real answer to them. The test is whether you have one of these problems, described concretely, right now.
What they cost
Every in-process call that becomes a network call acquires latency, partial failure, retries, and a serialisation format to version. Transactions that were atomic become distributed sagas with compensation logic. A stack trace becomes a distributed trace, assuming someone built that.
This cost is worth paying for the problems above. It is a poor trade for a codebase that is merely large.
Modularity first
Most of what people want from services — clear ownership, bounded blast radius, the ability to reason about a part in isolation — is available inside a single deployable. Enforce module boundaries, forbid reaching into another module's internals, and give each one a real interface.
This is also the honest prerequisite. If you cannot maintain boundaries within one codebase, distributing it will not impose the discipline; it will only make each violation more expensive to fix.
Split along the seams that exist
When splitting is genuinely warranted, extract the parts that are already loosely coupled, one at a time, keeping the system running throughout. A big-bang decomposition combines the risk of a rewrite with the operational complexity of distribution, which is close to the worst available combination.
A well-structured monolith deployed six times a day is a better place to be than a distributed system deployed monthly. Architecture should be chosen against a named constraint, not against a diagram that looks more modern.

