Few technical debates generate more heat and less light than microservices versus monolith. Advocates of microservices point to Netflix, Uber, and Amazon as proof that distributed architectures unlock the scalability and development velocity that modern businesses demand. Skeptics—increasingly vocal as the industry accumulates experience with distributed systems failures—cite the operational complexity, latency overhead, and distributed transactions nightmare as evidence that microservices are misapplied more often than not.
Both sides are right about something. Neither is right about everything. The question is not “which architecture is better?” It is “which architecture is appropriate for our specific situation, at this moment, given our team’s capabilities and our system’s requirements?”
This guide is a framework for making that decision rigorously rather than ideologically.
What the Architecture Actually Determines
Before choosing an architecture, understand what the choice actually determines—and what it doesn’t.
What architecture affects:
- How teams can work independently without coordination overhead
- How individual components can be scaled independently
- How failures in one area propagate to others
- The operational complexity of the system
- The surface area for security incidents
- How difficult it is to change domain boundaries over time
What architecture does not determine:
- Whether your software is well-engineered
- Whether your team ships quickly
- Whether your system is reliable
- Whether your costs are under control
A well-structured monolith outperforms a poorly designed microservices architecture on every meaningful dimension. The converse is also true. Architecture is a foundation, not a substitute for engineering discipline.
The Case for Starting with a Monolith
The software industry’s drift toward microservices as the default architecture choice obscures an important truth: monoliths are not the opposite of good engineering. They are a legitimate architectural choice that is optimal in a wide range of situations.
Monoliths excel when:
-
Domain boundaries are unclear — Microservices require stable, well-understood domain boundaries to function well. A startup or new product that hasn’t yet learned its domain will draw those boundaries incorrectly, and the cost of redrawing them in a distributed system is extraordinarily high. A monolith’s boundaries live in code and can be refactored; a microservice’s boundaries live in contracts, APIs, and deployment infrastructure and are much harder to change.
-
The team is small — The cognitive overhead of managing 15 independent services—separate deployments, separate monitoring, separate databases, network failures to handle—is justified when each service is owned by a dedicated team. A 4-person engineering team operating 15 microservices is drowning in operational overhead that contributes nothing to customer value.
-
Operational maturity is developing — Running distributed systems well requires observability infrastructure (distributed tracing, centralized logging, correlation IDs), sophisticated deployment tooling, and the operational experience to diagnose failures that span multiple services. Organizations without this capability will struggle.
-
Transactions are complex — Operations that require ACID transactions across multiple data entities are straightforward in a monolith with a single database and deeply complex in a microservices architecture requiring distributed saga patterns, eventual consistency, and compensating transactions.
“The dirty secret of the microservices revolution is that most of the companies evangelizing it—Netflix, Amazon, Uber—started as monoliths and decomposed over years as their scale and team size justified the investment. They didn’t start with microservices. They evolved to them.”
The Case for Microservices
Microservices are not architectural fashion—they solve real problems that emerge at certain scales of team size and system complexity. The key is understanding what those problems are before assuming they apply to you.
Microservices excel when:
-
Independent deployment velocity is critical — When different parts of the system need to deploy on different cadences, with different risk tolerances, a monolith becomes a coordination bottleneck. A payment service that deploys monthly and a recommendation engine that deploys hourly have fundamentally different risk profiles that a monolith cannot cleanly accommodate.
-
Scale requirements are heterogeneous — If your image processing service needs 10x the compute of your user authentication service, scaling them independently is dramatically more cost-efficient than scaling a monolith that carries both.
-
Teams need autonomous ownership — Conway’s Law—that systems tend to mirror the communication structures of the organizations that build them—works in both directions. Microservices enable team autonomy by giving teams full ownership of bounded domains. This reduces cross-team coordination overhead at the cost of distributed systems complexity.
-
Technology heterogeneity is valuable — When different problems genuinely benefit from different languages, frameworks, or data stores, microservices enable that heterogeneity. A real-time streaming component written in Rust, a data processing pipeline in Python, and a user interface in TypeScript can coexist in a well-designed service mesh.
The Decision Framework
Use these questions to structure the architecture decision:
1. What is the size and structure of the engineering team? Under 10 engineers: monolith is almost always correct unless there’s a compelling specific reason for service separation. 10–50 engineers: evaluate based on domain clarity and deployment velocity needs. Over 50 engineers: microservices alignment with team structure becomes increasingly valuable.
2. How well-understood is the domain? New product or domain: monolith until domain boundaries stabilize. Established domain with clear bounded contexts: microservices is viable if team size supports it.
3. What are the actual scale requirements? Uniform scale (all components scale together): monolith is fine. Heterogeneous scale (specific components need independent scaling): evaluate microservices for those specific components only.
4. What is the deployment risk tolerance? Uniform risk tolerance: monolith with good CI/CD. Fundamentally different risk profiles per domain: microservices enable appropriate isolation.
5. What is the team’s distributed systems experience? Limited distributed systems experience: monolith reduces operational risk. Strong distributed systems capability with observability tooling in place: microservices is operationally viable.
The Modular Monolith: The Architecture Most Organizations Should Actually Choose
The microservices-vs-monolith debate presents a false binary. The most appropriate architecture for the majority of organizations is the modular monolith—a single deployable unit internally organized into well-bounded, loosely coupled modules with clear APIs between them.
A modular monolith delivers:
- Internal domain boundaries enforced by module visibility rules (packages in Java, modules in Python, namespaces in .NET)
- Shared deployment simplicity without the operational overhead of distributed systems
- Refactoring path — modules with stable boundaries can be extracted into independent services later, when team size and scale requirements justify it
- Testability — integration testing a modular monolith is dramatically simpler than testing a distributed service mesh
The modular monolith is the architecture Sam Newman—author of Building Microservices—recommends as the default starting point. It is also, notably, the architecture most organizations have failed to achieve on their way to the distributed systems complexity they were sold as the solution.
When to Decompose: Migration from Monolith to Microservices
For organizations operating monoliths that have accumulated the team size and scale requirements that justify decomposition, the migration strategy matters as much as the target architecture.
Anti-pattern: Big-bang rewrite Attempting to rewrite the entire system as microservices simultaneously is a reliable path to a multi-year project that ships late, costs more than projected, and frequently results in feature regression. The investment is enormous and the risk is concentrated at the point of cutover.
Recommended pattern: Strangler Fig Extract services incrementally from the edges of the monolith, using an API gateway or facade layer to route traffic to either the monolith or the new service for each domain. The monolith shrinks gradually as services are extracted; the business continues operating throughout. Each extraction is independently testable, deployable, and reversible.
Service extraction priority order:
- High-change domains — services that change most frequently benefit most from independent deployment
- Scale outliers — components that need to scale dramatically differently from the rest of the system
- Team ownership alignment — components that a dedicated team can fully own without cross-team coordination
- Risk isolation — components where failure should not cascade to unrelated functionality
Operational Requirements You Must Satisfy Before Decomposing
No organization should decompose a monolith into microservices without having these operational capabilities in place:
- Distributed tracing — the ability to follow a request across service boundaries using correlation IDs (Jaeger, Zipkin, AWS X-Ray)
- Centralized logging — all service logs aggregated into a searchable platform (ELK stack, Splunk, Datadog)
- Service mesh or API gateway — traffic management, circuit breaking, and retry logic managed at infrastructure level, not application level
- Automated deployment pipeline — each service needs its own CI/CD pipeline; manual deployment of 20 services is not operationally sustainable
- Contract testing — consumer-driven contract tests (Pact) to catch API breaking changes before they reach production
Organizations that decompose without these capabilities discover that diagnosing a production incident in a distributed system without observability tooling is orders of magnitude harder than in a monolith.
Conclusion
The right architecture is the one that matches your team’s size and capability, your domain’s maturity, and your system’s actual scale requirements—not the one that the most prominent technology companies use. Start simpler than you think you need. Build domain boundaries in code before you build them in service contracts. Extract services when you have specific, validated reasons to do so—not because microservices are modern.
The organizations building the best software aren’t the ones who chose the most sophisticated architecture. They’re the ones who chose the most appropriate one and executed it with discipline.

