A monolith does not become obsolete when traffic grows, the codebase gets large, or the application moves to the cloud. It becomes a constraint when parts of the system need to change, scale, fail, or be owned independently and the existing deployment boundary prevents that independence. Until then, microservices often replace manageable application complexity with harder distributed-system complexity.
Do not split an application because the monolith is large. Split when a specific boundary needs operational independence that the monolith can no longer provide economically.
- The real decision is not monolith versus microservices
- What each architecture actually optimizes
- Start by asking what problem the split would solve
- 1. One capability needs to scale independently
- 2. Teams are blocked by a shared release lifecycle
- 3. The application contains a meaningful fault-isolation boundary
- 4. A business capability has become an independent ownership domain
- Why microservices cost more than their containers
- Data is usually the hardest boundary
- What should you extract first?
- Signals that should not trigger a split by themselves
- A practical architecture threshold
- The economics favor delaying irreversible complexity
- Choose the smallest architecture that preserves the independence you need
- Continue exploring scalable application architecture
The real decision is not monolith versus microservices
Architecture discussions often frame the choice as binary: build one application or build many services. That framing misses the more useful question. The decision is really about where to place boundaries and how expensive those boundaries should be.
A well-structured monolith can contain clear domain modules, enforce internal interfaces, run multiple instances behind a load balancer, use queues and caches, and scale to substantial workloads. It can also be deployed frequently if the engineering organization has reliable automation.
Microservices make selected boundaries stronger by turning in-process calls into network interactions and giving services separate deployment and ownership lifecycles. That can be valuable. It also means that failures, transactions, observability, API compatibility and debugging must now cross process boundaries.
The right baseline, therefore, is usually not an unstructured monolith. It is a modular monolith: one deployable application whose internal architecture deliberately separates business capabilities.
A process boundary is an expensive way to enforce a software boundary. Use it when the operational benefit is worth the distributed-system cost.
What each architecture actually optimizes
| Architecture | Primary advantage | Main constraint | Best fit |
|---|---|---|---|
| Modular monolith | Low coordination and operational overhead | Modules share one deployment and usually one scaling unit | Systems whose capabilities mostly change and scale together |
| Coarse-grained services | Isolation around a few important domains | Some distributed complexity without extreme service proliferation | Applications with several clear operational boundaries |
| Microservices | Independent deployment, ownership and scaling | High platform, network, data and observability complexity | Systems where multiple capabilities genuinely need independent lifecycles |
This spectrum matters because many applications do not need dozens of services. A system might benefit from extracting only payments, media processing, search or another operationally distinct capability while keeping most business logic inside a monolith.
That architecture is not an incomplete migration. It may be the correct end state.
Start by asking what problem the split would solve
A service extraction should have a measurable architectural purpose. “The codebase is getting big” is usually too vague. “The image-processing workload needs twenty times the compute of the rest of the application and forces us to scale everything together” is actionable.
Four signals are particularly useful because they describe constraints created by the existing boundary rather than preferences about architecture style.
1. One capability needs to scale independently
A monolith is commonly replicated as one unit. If most requests are inexpensive but one capability consumes disproportionate CPU, memory, connection capacity or specialized hardware, scaling the complete application may become inefficient.
An independent service can allow that workload to scale according to its own demand profile. The important qualification is that the economic difference must be meaningful. Separating a lightly used endpoint merely because it has different traffic does not automatically reduce infrastructure cost.
If independent scaling does not materially change capacity requirements or cost, scaling alone is a weak reason to introduce a service boundary.
2. Teams are blocked by a shared release lifecycle
A deployment boundary becomes expensive when unrelated teams repeatedly have to coordinate releases, regression testing or rollback decisions. If one domain changes several times a day while another is deliberately stable, forcing both through the same lifecycle can reduce delivery autonomy.
But deployment pain does not prove that microservices are required. Slow tests, fragile build pipelines, poor module boundaries and manual releases should be repaired directly. Turning each module into a service while retaining tightly coordinated deployments creates distributed architecture without independent delivery.
A useful extraction candidate is a capability that already has clear ownership, changes for its own reasons and can be deployed without requiring synchronized changes elsewhere.
3. The application contains a meaningful fault-isolation boundary
Some failures should not consume the availability budget of the entire application. Recommendation generation, reporting, document conversion or asynchronous notification delivery may be less critical than authentication, checkout or transaction processing.
Separating such workloads can create useful resource and failure isolation. The service can have its own concurrency limits, queue, retry policy, scaling rules and deployment strategy.
Yet distribution does not create resilience automatically. A supposedly optional microservice that every request synchronously depends on can make availability worse. Network calls add new failure modes, and long synchronous dependency chains increase the number of components that must succeed before a request can complete.
4. A business capability has become an independent ownership domain
The strongest service boundaries often follow the domain rather than the infrastructure. A capability that has its own terminology, data rules, roadmap and team is a better candidate for separation than an arbitrary technical layer such as “controllers,” “database access” or “utilities.”
This is why service decomposition tends to work better when the domain is understood. If boundaries are still changing rapidly, moving them across the network makes refactoring more expensive. Inside a monolith, moving a class or changing an internal interface is relatively cheap. Between independently deployed services, the same redesign may require API compatibility, data migration and coordinated rollout.
Separate business capabilities, not code folders. A service should own a coherent responsibility that can evolve with limited coordination outside its boundary.
Why microservices cost more than their containers
The infrastructure bill is only part of the microservice premium. The larger cost is operational.
In a monolith, a function call usually either returns or throws an error. Across services, the same interaction can time out, be retried, arrive twice, succeed after the caller has given up, encounter an incompatible API version or fail because an intermediate dependency is unavailable.
Observability changes as well. A production request may cross an API gateway, several services, a message broker and multiple data stores. Logs alone no longer describe what happened. Teams need consistent correlation identifiers, metrics, distributed tracing and service-level ownership to diagnose failures efficiently.
Deployment automation also stops being optional. Independent services are useful only if teams can build, test, release, monitor and roll them back safely. Without mature delivery tooling, the organization can end up operating many applications while still coordinating releases as though it had one.
The same multiplication occurs in security. Every service endpoint, workload identity, secret, network path and authorization relationship expands the surface that must be understood and maintained.
Data is usually the hardest boundary
Code decomposition is easier than data decomposition.
If two nominally independent services constantly read and modify the same database tables, they remain coupled through the schema. A database change can require coordination across services, undermining one of the principal reasons for making them independent.
Giving services control over their own data improves autonomy but changes transaction design. A business operation that previously committed several updates in one local database transaction may now span separate services and persistence systems. The architecture must deal with partial completion, retries, duplicate messages and temporary inconsistency.
That can require patterns such as sagas, outbox messaging, idempotent consumers or compensating actions. Those patterns are useful where the business process genuinely crosses autonomous domains. They are unnecessary overhead when the data naturally belongs together.
If two proposed services cannot own their data and evolve without constant cross-service transactions, reconsider whether the boundary belongs across the network.
What should you extract first?
The safest first service is rarely the deepest, most interconnected part of the application. Look for a capability with a clear interface, limited dependencies and an obvious reason for independent operation.
Good candidates commonly include workloads that already behave asynchronously, integrate with external systems, have unusual resource profiles or are owned by a clearly distinct domain team.
A first extraction should also test whether the organization can operate distributed software effectively. The architecture now needs reliable deployment, monitoring, incident response, service ownership and compatibility practices. Discovering those requirements with three services is cheaper than discovering them with thirty.
For an existing monolith, incremental decomposition is generally safer than a full rewrite. A strangler-style migration routes selected functionality to a new service while the existing application continues to operate. Capabilities can then be extracted one at a time, with rollback paths preserved during the transition.
The first extraction is not only a software refactor. It is a test of whether your delivery and operations model is ready to own distributed systems.
Signals that should not trigger a split by themselves
Several commonly cited reasons for adopting microservices are symptoms that may have cheaper remedies.
- The repository is large. Repository size says little about whether deployment boundaries should change.
- Deployments are slow. First identify whether tests, builds, release processes or architectural coupling are actually responsible.
- The application needs more instances. A stateless monolith can be horizontally scaled without becoming a microservice architecture.
- Different modules use different technologies. Technology diversity can be useful, but it also increases maintenance burden and should solve a concrete requirement.
- The company expects growth. Design for the constraints you can reasonably anticipate, but avoid paying permanent distributed-system costs for hypothetical scale.
Another weak signal is organizational fashion. Hiring teams into a microservice architecture does not automatically create team autonomy. If services constantly depend on synchronous changes in other services, the organization has merely moved coordination from the repository into APIs and deployment pipelines.
A practical architecture threshold
Instead of asking whether the application is “big enough” for microservices, evaluate whether a candidate capability has accumulated enough reasons to deserve independence.
| Question | If the answer is no | If the answer is yes |
|---|---|---|
| Does it need materially different scaling? | Keep shared capacity | Independent scaling may justify extraction |
| Does it need an independent release cadence? | A shared deployment is simpler | A separate deployment boundary may help |
| Can one team clearly own it? | The boundary may be premature | Ownership supports service autonomy |
| Can its interface remain reasonably stable? | Keep refactoring in process | A network contract becomes more practical |
| Can it control its data without constant distributed transactions? | The proposed boundary is probably too coupled | Data autonomy strengthens the case |
| Would failure isolation materially improve the system? | Distribution may only add failure modes | Isolation can justify operational separation |
No single “yes” proves that a service is required. Several aligned signals create a stronger case. A capability that needs different scaling, has its own team, owns its data and changes independently is far more compelling than one extracted solely because its source directory has grown large.
The economics favor delaying irreversible complexity
A modular monolith concentrates operational cost. One application runtime, one primary deployment pipeline and fewer network boundaries reduce the amount of platform machinery needed to operate the system.
Microservices distribute both workloads and overhead. More services can mean additional runtime capacity, load balancing, networking, telemetry, deployment automation and engineering effort. Managed cloud services can reduce the burden of operating some of this infrastructure, but they do not eliminate architectural coordination or distributed failure modes.
The economic advantage appears when independent operation saves more than it costs. Selective scaling may reduce compute waste. Independent delivery may reduce coordination time. Fault isolation may reduce the business impact of incidents. Clear ownership may allow teams to move faster.
Those are architectural returns. Service count is not.
Keep the application together while its important parts benefit from sharing one lifecycle. Split only the boundaries whose need for independence is stronger than the cost of distributing them.
Choose the smallest architecture that preserves the independence you need
For many applications, the right starting point and long-term architecture is a modular monolith. It keeps calls local, transactions straightforward and operations concentrated while still allowing teams to build strong internal boundaries.
When a specific capability develops a different scaling profile, ownership model, release cadence, reliability requirement or data lifecycle, extracting it can be justified. The result does not need to be a wholesale microservice transformation. A monolith surrounded by several autonomous services can be a mature architecture rather than an intermediate state.
Microservices are most useful when they formalize independence that already exists in the problem. Before that point, they manufacture independence the organization must pay to maintain.
Continue exploring scalable application architecture
Follow the decision from service boundaries into infrastructure economics, performance and migration strategy.
Cloud strategy, architecture and infrastructure decisions explained without vendor noise.
