May. 04, 2026
23 minutes read
Share this article
Last Updated July 2026
For most of the last decade, microservices were treated as the natural endpoint of any serious engineering organization. Splitting a system into small, independently deployable services was framed as a rite of passage, the thing you did once you were big enough or ambitious enough to matter. The monolith, meanwhile, became a slur. If your system ran as one deployable unit, the assumption was that you had not gotten around to fixing it yet.
That framing has aged badly. The teams shipping fastest are not the ones with the most services. Years of DORA research on software delivery performance point to practices like small batch sizes and loose coupling rather than to any particular deployment topology. In several high-profile cases, the fastest teams are ones that pulled services back together after discovering that distribution bought them complexity they never needed. The real question was never monolith or microservices as an identity. It is a question of constraints: what does your system actually need to do, how many teams need to move independently, and where does the cost of a network hop stop being worth it?
This guide lays out the trade-offs without the ideology. It covers what each architecture genuinely costs, when decomposition pays off, when it quietly bankrupts your velocity, and how to decide using a framework rather than a trend.
Choose a monolith or a modular monolith by default, and reach for microservices only when a specific, measured constraint demands one: many teams that must deploy independently, capabilities with genuinely different scaling profiles, or a critical path that needs hard fault isolation. Microservices are a tool for buying team autonomy and independent scaling, and you pay for that tool in latency, operational complexity, and cognitive load. If you cannot name the constraint a service boundary solves, you are not ready to draw it. The rest of this guide explains why, and gives you a framework to make the call.
A monolith is an application that is built, deployed, and run as a single unit. The user interface, the business logic, and the data access layer live in one codebase and ship in one artifact. When you deploy, you deploy the whole thing. When it scales, you run more copies of the entire application.
The word carries baggage it does not deserve. A monolith is not automatically a tangle of spaghetti. A well-structured monolith has clear internal boundaries, disciplined module separation, and a codebase a new engineer can clone and run in an afternoon. What defines it is not messiness but topology: everything runs in one process, so calls between components are function calls rather than network requests. That single fact is the source of most of its advantages and most of its perceived limitations.
A microservices architecture decomposes an application into a set of small, independently deployable services, each owning a slice of the business capability and, ideally, its own data. The cleanest boundaries follow bounded contexts from domain-driven design, where each service maps to a distinct part of the business rather than to a technical layer. Services communicate over the network, usually through HTTP, gRPC, or asynchronous messaging, often behind an API gateway. Each service can be built, tested, deployed, and scaled on its own schedule by its own team.
The appeal is organizational as much as technical. The model pairs naturally with small, autonomous teams, the two-pizza teams Amazon made famous, where each team owns a service end to end. When boundaries are drawn well, teams stop stepping on each other. A payments team can ship twice a day without waiting for the search team to finish a feature. The trade-off is that everything which used to be a function call is now a distributed system problem, with all the latency, partial failure, and coordination cost that implies.
If you want the practitioner-level view of how AI is reshaping who makes these calls, our piece on the evolution of the AI-native developer is a useful companion to this decision.
The two architectures are not opposites on a single axis. They make different trade-offs across at least eight dimensions, and a mature decision weighs all of them rather than fixating on scalability alone.
| Dimension | Monolith | Microservices |
|---|---|---|
| Deployment | One artifact, one pipeline, one release | Many artifacts, independent releases, orchestration required |
| Local development | Clone, run, done | Requires service mocks, containers, or a shared remote environment |
| Fault isolation | A bad path can affect the whole process | Failure is contained, but partial failures are harder to reason about |
| Data consistency | Transactions inside one database | Eventual consistency, sagas, and compensating actions |
| Scaling | Scale the whole app, even hot paths only | Scale each service to its own load profile |
| Observability | Stack traces mostly tell the story | Distributed tracing, correlation IDs, and log aggregation are mandatory |
| Team topology | Works well up to a few teams on one codebase | Enables many autonomous teams once boundaries are stable |
| Cognitive load | One mental model | Every engineer carries a map of the network |
Notice that most of the microservices column describes work you take on, not power you gain for free. That is the honest shape of the trade-off. Distribution is a tool for buying team autonomy and independent scaling, and you pay for it in operational complexity whether or not you end up needing what you bought.
The pitch for microservices usually emphasizes benefits and treats the costs as implementation details. In practice, the costs are structural, and they compound.
An in-process function call takes nanoseconds. A network call to another service, even inside the same data center, takes milliseconds, and it can fail. When a single user request fans out across eight or ten services, those millisecond costs and failure probabilities stack. A request that would have been trivially fast in a monolith becomes a coordination problem, and tail latency, the slow ninety-ninth percentile that users actually feel, gets worse with every hop you add.
In a monolith, a stack trace usually tells you what happened. In a distributed system, a single request touches many services, so you need correlation IDs, distributed tracing, and centralized log aggregation just to answer the question of where a request slowed down or broke. A service mesh such as Istio or Linkerd can standardize this, but it is another layer to run and reason about. Defining and defending service-level objectives across a dozen services is real engineering work, and its cost scales with the number of services and the volume of telemetry they emit.
A monolith can wrap a multi-step operation in a single database transaction. Split those steps across services with separate databases, and that guarantee disappears. You move into the world of eventual consistency, sagas, and compensating transactions, where correctness is something your application code has to orchestrate rather than something the database enforces for you. This is where a large share of subtle production bugs in microservices systems actually live.
Every service needs its own runtime, its own baseline capacity, and often a sidecar for the service mesh. Independent scaling is efficient in theory, but idle headroom reserved per service, plus the mesh and platform layers, frequently means a distributed system consumes more total compute than the monolith it replaced, especially before traffic justifies the granularity.
The energy and cost implications of that footprint are worth taking seriously; we go deeper in green coding and sustainability in software.
The cost that does the most damage is the one that never appears on an invoice. In a monolith, an engineer who needs to change how two capabilities interact opens one codebase, makes the change, and ships it. In a distributed system, that same change may touch three repositories, require an agreed contract between two teams, and land through three separate deployments coordinated across three on-call rotations. The engineering hours are only part of it. The higher cost is latency in the human system: decisions that used to take an afternoon now take a sprint because they have to travel through interfaces between teams.
This tax scales with the number of services, and it is why organizations sometimes feel slower after decomposition even though each individual team is technically more autonomous. Autonomy is only a gain when teams rarely need to cross boundaries. If your boundaries are wrong, distribution turns every ordinary change into a coordination project, and the velocity you were promised evaporates into meetings.
The most instructive examples of the last few years are not teams adopting microservices. They are teams reversing the decision or never making it in the first place, at serious scale.
In 2023, the Amazon Prime Video engineering team published an account of rebuilding their audio and video quality monitoring service, a decision that drew wide industry attention. The original design was a distributed system of serverless components. As volume grew, the cost of passing data between those components and the scaling limits of the orchestration became the bottleneck. The team consolidated the pieces into a single process and reported a 90 percent reduction in infrastructure cost, while also raising the ceiling on the scale they could handle.
The lesson is not that serverless or microservices are wrong. It is that a granular distributed design was the wrong fit for a workload dominated by moving large amounts of data between tightly coupled steps. The architecture followed the constraint once the team measured it.
Stack Overflow has long been the counterexample to the idea that high traffic forces you into microservices. As its engineer Nick Craver documented in a widely cited breakdown of the Stack Overflow architecture, a single application served every Stack Exchange site from just nine primary web servers, handling billions of requests a month while most of that hardware sat below two percent CPU. Their engineering writing has consistently emphasized performance discipline and vertical simplicity over horizontal decomposition. A well-built monolith on capable hardware absorbed a load that many teams would assume requires a sprawling service mesh.
When Meta launched Threads in 2023 and reached over 100 million sign-ups within five days, the fastest-growing consumer app launch on record at the time, the team did not stand up a fresh microservices estate. They built on Instagram’s existing infrastructure and codebase, reportedly shipping the app in around five months by reusing a mature platform. The takeaway for smaller organizations is blunt: leverage and familiarity beat architectural purity when time to market is the binding constraint.
These cases are not isolated. Across the industry, there is a visible correction underway, sometimes called reconsolidation or the return to modular monoliths. Teams that decomposed early, before their organization or their domain boundaries were stable, are pulling services back together. The pattern is consistent: premature decomposition froze the wrong boundaries into the network, and every change that crossed those boundaries became a multi-team, multi-deploy coordination exercise. Merging the chatty services back into a single process restored the ability to make a change and ship it in one step.
What makes the correction healthy rather than embarrassing is that it treats architecture as reversible. A decision to split a service is not a vow, and neither is a decision to merge one back. The teams that recover well from premature decomposition are the ones that measured the pain, identified the specific services that were coupled in practice despite being separate on paper, and consolidated those while leaving genuinely independent services alone. Reconsolidation is not a retreat to the monolith as an ideology; it is the same constraint-driven reasoning that should have governed the split in the first place, applied with better data.
This mirrors a broader lesson about modernization we cover in modernization is not a project, it is a posture: the goal is not to reach a fashionable end state but to keep the system aligned with current constraints.
None of this is an argument against microservices. It is an argument against adopting them by default. There are conditions under which distribution earns its cost, and when several of them hold at once the case becomes strong.
The inverse conditions favor keeping things together, and they describe far more organizations than most architecture debates admit.
The framing of monolith versus microservices hides the option that suits most teams best. A modular monolith keeps everything in one deployable unit while enforcing strict internal boundaries between modules, each owning its data and exposing a clear internal interface. You get the operational simplicity of a single deployment and the clean separation that makes future extraction possible, without paying the network tax before you need to.
The strategic value is optionality. Well-defined modules are the natural seams you would carve along later if a specific module genuinely needs to become a service. You defer the distribution cost until a real constraint, an independent scaling need or a team-autonomy bottleneck, forces the decision, and by then you have data rather than a guess. For a large majority of systems, a disciplined modular monolith is the correct destination, not a way station.
The discipline is the hard part, and it is where most attempts fail. A modular monolith only works if the boundaries are enforced, not merely documented. That means modules communicate through explicit interfaces rather than reaching into each other’s data, that a shared database is partitioned by module ownership even when it is one physical instance, and that build tooling actively prevents a module from importing another module’s internals. Without that enforcement, a modular monolith quietly decays into the tangled kind everyone fears, and the option value you were preserving disappears. The good news is that these guardrails are cheap compared to operating a service mesh, and they pay for themselves the first time you need to extract a module cleanly.
There is a failure mode worse than either pure option, and it is where premature decomposition usually lands: the distributed monolith. This is a system that has been split into separate services across the network but whose parts remain so tightly coupled that they cannot actually be deployed or changed independently. You pay the full operational tax of distribution, the latency, the tracing, the eventual consistency, the multiplied infrastructure, while getting none of the autonomy that was supposed to justify it.
The symptoms are unmistakable once you know them. A single feature requires several services to be released together in a specific order. Two services share a database table, so a schema change ripples across team boundaries. Services call each other synchronously in long chains, so one slow dependency degrades the whole request path. If your services must be deployed in lockstep, you do not have microservices. You have a monolith that someone ran through a network cable, and the right move is almost always to merge the coupled services back into one process and redraw the boundaries only where they are genuinely independent.
If you are already on the microservices path, the more useful question is often when to stop. These are the signals that you have gone past the point of positive returns.
If several of these are true, the fix is not more tooling. It is merging the services that never should have been separate.
Conway’s Law observes that organizations design systems that mirror their own communication structure. If four teams build a system, you tend to get four major components with the seams falling on team boundaries. This is not a bug to be engineered away; it is a gravitational force. Fighting it produces architectures that constantly fray at the edges.
The Inverse Conway Maneuver turns this into a deliberate tool: shape the teams to match the architecture you want, so that the system’s boundaries emerge naturally from how people are organized. Large engineering organizations have learned, often painfully, that architecture and org design are the same decision viewed from two angles. Draw service boundaries that cut across how your teams actually communicate and every cross-boundary change becomes a negotiation. Align the two and the friction largely disappears.
When extraction is genuinely warranted, the safest route is incremental, not a rewrite. The strangler fig pattern, named by Martin Fowler after a vine that grows around a tree until it can stand on its own, works by routing traffic through a facade and gradually replacing pieces of the monolith with new services behind it. The old system keeps running until each capability has been fully and safely replaced, at which point the corresponding part of the monolith is removed.
Fowler’s original write-up of the strangler fig application remains the definitive reference, and the same discipline applies in reverse when you consolidate services back into a monolith.
Sequencing matters as much as the pattern. Extract the capability with the clearest boundary and the least shared state first, not the one under the most pressure. An early, clean extraction proves the facade, the deployment pipeline, and the observability plumbing on a low-risk target, so that by the time you reach the genuinely tangled parts of the system you are exercising machinery you already trust. Teams that begin with their hardest, most entangled module tend to stall, because they are debugging the extraction mechanism and the domain complexity at the same time.
The pattern that consistently fails is the big-bang rewrite: freeze the old system, build the new one alongside it, and cut over on a date. It fails because the old system keeps changing while you build, the new one accretes its own gaps, and the cutover concentrates all the risk into a single moment. Incremental extraction spreads risk out and keeps you shippable throughout, which is exactly why the same facade-and-replace discipline is the right way to consolidate services back together when the decision runs the other direction.
Instead of arguing from preference, score the decision. Rate your system from one to five on each of the following, where a higher score points toward services and a lower score points toward a monolith or modular monolith.
If your scores cluster low, a modular monolith is almost certainly right. If they cluster high across several axes, targeted extraction of the specific capabilities that scored highest is justified. A uniformly middling result is the strongest possible argument for the modular monolith: keep one deployable unit, enforce hard module boundaries, and extract only when a single axis spikes.
A worked example makes the framework concrete. Imagine a forty-engineer company across five teams, running a product whose domain has stabilized over three years, with one compute-heavy analytics capability that already scales differently from everything else, and a mature CI/CD and observability stack. Team independence scores high, domain stability scores high, and one capability has a clearly divergent scaling profile, while fault isolation is moderate. That profile does not argue for decomposing the whole system. It argues for extracting exactly one service, the analytics capability, and leaving the rest as a modular monolith. The framework points to a surgical move rather than a wholesale migration, which is almost always the right shape of answer.
For leaders framing this inside a broader technology agenda, our overview of the trends defining how organizations execute in 2026 sets the context in which these architecture calls get made.
Architecture debates fixate on request latency and lines of code and ignore the costs that dominate the balance sheet over a system’s life. If you are comparing options, measure the full picture rather than the demo.
| Cost category | What actually drives it in a distributed system |
|---|---|
| Compute and runtime | Redundant service instances, sidecars, and idle capacity reserved for isolation |
| Network | Inter-service calls that were once in-process function calls, plus egress between zones |
| Observability tooling | Tracing, metrics cardinality, and log volume that scale with the number of hops |
| Platform engineering | The internal team required to keep the paved road usable for everyone else |
| Cognitive overhead | Onboarding time, incident coordination, and the slow tax of cross-service changes |
The category that surprises teams most is cognitive overhead. It does not appear on a cloud invoice, but the slow tax of every engineer needing to hold a map of the network in their head, and every incident requiring several teams to coordinate, is often the highest real cost of a distributed system. A monolith’s single mental model is an asset that rarely shows up in a spreadsheet.
If your architecture is quietly blocking your AI ambitions rather than enabling them, identifying dead architecture is the next diagnostic to run.
No. A well-structured monolith with clear internal boundaries is a legitimate, often optimal, architecture. Technical debt comes from poor structure and missing discipline, not from running as a single deployable unit. Plenty of high-scale systems are monoliths by deliberate choice.
Yes, much further than the folklore suggests. Stack Overflow served billions of monthly page views from a small number of servers running a monolith. Vertical scaling and performance discipline take a well-built monolith well past the point most teams ever reach.
When you have a specific, measured constraint that a service boundary solves: independent deployment for many teams, a genuinely divergent scaling need, or fault isolation for a critical path. Migrate the capability that has the constraint, not the whole system, and do it incrementally.
A single deployable application with strictly enforced internal module boundaries, each module owning its data and exposing a clear interface. It gives you the operational simplicity of a monolith and clean seams for future extraction, without the network cost of full distribution.
Consider it if features routinely require coordinated changes across several services, if you maintain complex sagas to replace what one database did for free, or if your services are so chatty they are effectively one system with a network in between. Reconsolidation is a legitimate, increasingly common move.
Treat model inference and heavy AI processing as a candidate for a dedicated boundary because its scaling and hardware profile often diverges sharply from the rest of the app. That can mean a separate service or a well-isolated module. The decision follows the same framework: extract when the scaling or isolation constraint is real and measured.
The monolith versus microservices debate was never really about which architecture is better. It was about resisting the urge to answer an engineering question with a fashion decision. Microservices solve genuine problems of team autonomy, independent scaling, and fault isolation, and when those problems are real, distribution is worth its cost. When they are not, a monolith or a modular monolith will ship faster, cost less, and stay easier to reason about.
The teams doing this well in 2026 are not counting services. They are measuring constraints, drawing boundaries that match how their people actually work, and extracting only when a specific need forces the call. Stop breaking things apart because it looks like progress. Break them apart, or keep them together, because your constraints told you to.
Andrés Narváez is a Solutions Architect and head of the architecture team at Coderio, with over 10 years of experience in SaaS delivery, microservices, event-driven systems, data and cloud infrastructure. He holds a Master's in Computer Science and writes about software architecture and engineering team strategy.
Andrés Narváez is a Solutions Architect and head of the architecture team at Coderio, with over 10 years of experience in SaaS delivery, microservices, event-driven systems, data and cloud infrastructure. He holds a Master's in Computer Science and writes about software architecture and engineering team strategy.
Accelerate your software development with our on-demand nearshore engineering teams.