# Microservices vs Monolith: When to Split

> Should you break your monolith into microservices? Learn the real trade-offs, when each architecture wins, and migration strategies that don't break your team.

URL: https://www.matthewswong.com/en/blog/microservices-vs-monolith/

- Author: Matthews Wong (https://www.matthewswong.com/en/about/)
- Published: 2025-01-08
- Updated: 2025-01-08
- Category: Software Development

The microservices vs monolith debate is one of the most consequential architectural decisions a team makes. Get it wrong and you're either drowning in distributed systems complexity before you have a product-market fit, or struggling to scale a monolith that's become a tangled ball of mud. This post cuts through the hype to give you a framework for making the right choice — and a migration strategy if you need to change course.

## Understanding the Two Architectures

A monolith is a single deployable unit where all modules — users, orders, inventory, billing — share the same process and often the same database. A microservices architecture decomposes those modules into independently deployable services that communicate over a network. The difference sounds simple, but it has profound implications for your team, your tooling, and your operational burden.

### The Monolith: Simpler Than It Sounds

Monoliths get a bad reputation, but a well-structured monolith with clear module boundaries is an excellent starting point for almost any product. You have a single codebase to navigate, transactions that span multiple modules are trivially handled by the database, debugging is straightforward (one process, one log stream), and deployment is a single artifact. The operational burden is minimal compared to microservices.

### Microservices: Power With a Price

Microservices enable independent deployment and scaling of services, isolation of failures, and technology heterogeneity — different services can use different languages or databases. But each service boundary you add introduces network latency, the need for distributed tracing, eventual consistency challenges, and the complexity of coordinating deployments across multiple repositories and teams. You are trading development simplicity for operational flexibility.

> Sam Newman's 'Monolith to Microservices' recommends starting with a modular monolith — strong internal module boundaries enforced by code, not network calls. This gives you separation of concerns without network overhead, and makes a future migration significantly easier.

## When Each Architecture Wins

The right choice depends on your team size, deployment frequency, scaling requirements, and organizational structure. Conway's Law is real: your system's architecture will mirror your team's communication structure. Small teams building a single product almost always benefit from a monolith. Large organizations with multiple teams, distinct SLAs per service, and the ability to own independent deployment pipelines are the natural home of microservices.

### Choose a Monolith When

You have a small team (under 10 engineers), you're still searching for product-market fit and need to iterate quickly, your modules share significant data and cross-cutting concerns, you don't have a dedicated DevOps or platform team to manage service infrastructure, or you're building an internal tool or MVP where simplicity is paramount. A monolith lets you move fast and pivot without distributed system overhead.

### Choose Microservices When

Your teams are independently sized and structured around services, you need to scale specific components independently (your recommendation engine has very different compute needs than your billing service), different services have different reliability or compliance requirements, you need to support multiple deployment environments independently, or different parts of your system are owned by teams that shouldn't share deployment cycles.

## Migration Strategy: The Strangler Fig Pattern

If you're moving from a monolith to microservices, the strangler fig pattern is the safest approach. Rather than a big-bang rewrite (which almost always fails), you incrementally extract services from the monolith while keeping it running. An API gateway routes requests to either the monolith or the new microservice based on the path or feature flag. Over time, the monolith shrinks as services are extracted.

### Step-by-Step Migration

Start by identifying the bounded context most worth extracting — ideally one with clear API boundaries, independent scaling needs, and no tight data coupling to the rest of the monolith. Extract the data first: give the new service its own database. Then route traffic through the API gateway. Validate the new service under production load before removing the monolith code. Repeat for the next bounded context.

> **Distributed Monolith: The Worst of Both Worlds**

> The most dangerous anti-pattern is the distributed monolith: you've split your application into multiple services, but they're tightly coupled — they share a database, call each other synchronously in long chains, and must be deployed together. You've taken all the operational complexity of microservices with none of the benefits. Before extracting a service, ensure it can truly own its data and deploy independently.

### Data Migration Strategies

The hardest part of microservices migration is the data. Use the database-per-service pattern: each service owns its data and exposes it only via its API. Use event sourcing or change data capture (CDC) to synchronize data between the old monolith and new service during the transition period. Never let two services share a database table — it recreates the tight coupling you're trying to escape.

## Operational Considerations

Microservices shift complexity from your code to your infrastructure. You need service discovery, distributed tracing, centralized logging, health checks, circuit breakers, and an API gateway before your microservices architecture is production-ready. Budget time for this infrastructure work — it is not optional.

### Observability: The Microservices Lifeline

In a monolith, you have one log stream. In microservices, a single user request may touch ten services. Distributed tracing (OpenTelemetry + Jaeger or Tempo) gives you end-to-end visibility. Structured logging with correlation IDs lets you filter logs across services for a single request. Metrics and alerting per service (Prometheus + Grafana) tells you which service is the bottleneck.

### Service Mesh and API Gateway

A service mesh like Istio or Linkerd handles mTLS between services, load balancing, retries, timeouts, and circuit breaking at the infrastructure level — without changing application code. An API gateway (Kong, AWS API Gateway) handles authentication, rate limiting, and routing at the edge. Both are force multipliers for a microservices architecture but add their own operational complexity.

> Use health checks and readiness probes on every service from day one. A service that fails its readiness probe is removed from the load balancer automatically, preventing cascading failures before they reach users.

## Making the Decision

The monolith vs microservices decision is not permanent, and you shouldn't treat it as one. Start simple. Build a well-structured monolith. When a specific bottleneck or team ownership issue genuinely demands a service extraction, make it surgically, following the strangler fig pattern. Avoid microservices-by-default and premature decomposition — they are among the most common sources of unnecessary complexity in modern software teams.

| Dimension | Monolith | Microservices |
| --- | --- | --- |
| Deployment | Single deployable unit, one build pipeline | Independent deployments per service, higher release velocity but more coordination |
| Database | One shared database, simple transactions | Database per service, cross-service transactions need sagas or eventual consistency |
| Scaling | Scale the entire app even if only one module is under load | Scale only the bottleneck service independently |
| Team structure | Works well for small teams sharing one codebase | Matches Conway's Law, separate teams owning separate services |
| Operational complexity | Single log stream, one process to monitor | Needs distributed tracing, service mesh, and centralized logging |
| Failure isolation | One bug can crash the entire application | A failing service degrades gracefully if the rest stays resilient |
| Best fit | Early-stage products, small teams, unclear domain boundaries | Mature domains with clear bounded contexts and independent scaling needs |

## Source links

- https://martinfowler.com/articles/microservices.html
- https://martinfowler.com/bliki/MonolithFirst.html
- https://microservices.io/patterns/refactoring/strangler-application.html
- https://docs.microsoft.com/en-us/azure/architecture/patterns/strangler-fig

## FAQ

### When should a team choose a monolith over microservices?

A monolith is the right default for small teams of under 10 engineers, products still searching for product-market fit, or systems where modules share significant data and cross-cutting concerns. Without a dedicated DevOps or platform team to manage service infrastructure, a monolith avoids the operational overhead that microservices introduce before you need it.

### What is the strangler fig pattern and why is it safer than a big-bang rewrite?

The strangler fig pattern incrementally extracts services from a running monolith, routing traffic via an API gateway to either the monolith or the new microservice based on path or feature flag. The monolith shrinks as each service is validated under production load before the corresponding monolith code is removed. A big-bang rewrite almost always fails because it requires simultaneously replacing everything while keeping the system live.

### What is a distributed monolith and why is it the worst of both worlds?

A distributed monolith splits an application into multiple services but keeps them tightly coupled — they share a database, call each other synchronously in long chains, and must be deployed together. This takes all the operational complexity of microservices with none of the benefits such as independent scaling or deployment. Before extracting a service, it must be able to own its data and deploy independently.

### How should data be handled during a migration from monolith to microservices?

Each service should own its data exclusively and expose it only through its API — this is the database-per-service pattern. During the transition period, event sourcing or change data capture (CDC) can synchronize data between the old monolith and the new service. Two services sharing a database table must be avoided because it recreates the tight coupling that microservices are meant to escape.

### What operational tooling is required before microservices are production-ready?

Microservices require service discovery, distributed tracing, centralized logging, health checks, circuit breakers, and an API gateway before they are ready for production. Distributed tracing tools like OpenTelemetry with Jaeger or Tempo provide end-to-end visibility across services, while Prometheus and Grafana cover per-service metrics and alerting. A service mesh such as Istio or Linkerd can handle mTLS, retries, timeouts, and circuit breaking at the infrastructure level without changing application code.

---

- HTML version: https://www.matthewswong.com/en/blog/microservices-vs-monolith/
- Bahasa Indonesia: https://www.matthewswong.com/id/blog/microservices-vs-monolith/
- Site index for agents: https://www.matthewswong.com/llms.txt
- Sitemap: https://www.matthewswong.com/sitemap.xml
- API description (OpenAPI): https://www.matthewswong.com/openapi.json
