The microservices backlash is real. Teams that decomposed prematurely spent years rebuilding the monolith they started with, but distributed across a dozen services, with Kafka in the middle and three on-call engineers permanently debugging network timeouts. The architecture is not wrong — the timing and scale were. In 2026, the question is not "monolith or microservices?" but "when does the trade-off actually flip?"
What changed in 2026
- The modular monolith is formally mainstream — Sam Newman's updated patterns and the "majestic monolith" discourse have given teams permission to choose simplicity without career guilt.
- Kubernetes reduced microservice ops overhead — deployment automation is no longer the differentiator; the question is purely about team structure and scale.
- gRPC and Protobuf matured — if you do decompose, the inter-service communication story is better than the REST-over-HTTP era.
- Temporal and Inngest changed distributed workflow thinking — durable execution frameworks handle saga patterns and long-running workflows without you writing compensating transactions by hand.
- DORA metrics made the debate empirical — deployment frequency, lead time, and change failure rate are the actual measures. Many monolith teams score higher than comparable microservice teams.
Core trade-off table
| Dimension |
Monolith |
Microservices |
| Operational complexity |
Low |
High |
| Local development |
Simple |
Complex (Docker Compose or Tilt) |
| Deployment |
One unit |
N independent pipelines |
| Scaling |
Whole app scales together |
Services scale independently |
| Team autonomy |
Low (shared codebase) |
High (service ownership) |
| Inter-service calls |
In-process (fast) |
Network calls (latency + failure) |
| Distributed transactions |
Easy (ACID) |
Hard (sagas, eventual consistency) |
| Observability |
Simpler (one process) |
Requires distributed tracing |
| Hiring and onboarding |
Easier (one codebase) |
Harder (N codebases, N tech stacks) |
| Correct for < 5 engineers |
Yes |
No |
| Correct for > 50 engineers |
Usually no |
Usually yes |
The modular monolith architecture
This is the pattern most 5–50 engineer teams should default to:
my-app/
modules/
billing/
billing.service.ts
billing.repo.ts ← own DB schema/tables
billing.api.ts ← internal API contract
orders/
orders.service.ts
orders.repo.ts
orders.api.ts
notifications/
...
shared/
auth/
config/
main.ts ← single deployment unit
Key rules:
- Modules do not import each other's internals — only their exported API contract
- Each module owns its database tables (even if it's one shared Postgres instance)
- Modules communicate via well-defined interfaces, not direct function calls across boundaries
When you eventually split, each module becomes a service with minimal refactoring.
// BAD: billing directly reads orders table
const revenue = await db.orders.sum('amount'); // billing importing orders internals
// GOOD: billing calls orders API contract
const revenue = await ordersApi.getTotalRevenue({ since: startDate });
When microservices actually pay off
- Teams ship at different cadences — the auth team releases daily, the billing team releases monthly. A shared deploy unit forces coordination.
- Independent scaling requirements — your image processing service needs 64-core GPU instances; your auth service needs a 256 MB container. Same deployment unit cannot accommodate both.
- Polyglot requirements — a specific service genuinely benefits from a different language or runtime (e.g., a Rust service for performance-critical processing, Python for ML inference).
- Regulatory isolation — PCI-DSS scope reduction often requires card-processing logic in a separate, audited deployment unit.
- Team count — Conway's Law is real. When you have 10+ teams, the org structure will force service boundaries whether you plan for them or not.
Common mistakes
Splitting by technical layer (data service, logic service, presentation service). This creates chatty inter-service calls for every request. Split by business domain, not technical function.
Using synchronous REST calls for everything. Service A calling B calling C via HTTP means three failure points per request. Use async messaging (Kafka, SQS) for operations that do not need immediate results.
Shared database across microservices. The fastest way to couple your services is a shared schema. Each service must own its data store.
# Anti-pattern: shared DB
orders-service ─┐
billing-service ─┼──► shared postgres (orders, billing, users schema)
users-service ─┘
# Correct: separate data ownership
orders-service ──► orders-db (postgres)
billing-service ──► billing-db (postgres) + billing-events (kafka)
users-service ──► users-db (postgres)
No distributed tracing. Once you have five services, a slow request is untraceable without trace IDs and a tool like Jaeger, Tempo, or AWS X-Ray.
What to skip
- Microservices for a team of three — the operational overhead will consume all engineering capacity.
- Event sourcing as a default — it solves real problems at large scale but adds significant complexity. Use it when audit trails and temporal queries are requirements, not by default.
- Service meshes before you need them — Istio and Linkerd are powerful but complex. A single Kubernetes cluster with network policies and cert-manager handles most small-to-medium service meshes adequately.
FAQ
How do I know when my monolith is ready to split?
When deploys to one domain are blocked by unrelated work in another, or when a specific service's resource profile diverges from the rest, or when a team's autonomy is meaningfully constrained by the shared codebase.
What is the strangler fig pattern?
A migration strategy: new features are built as external services, and old monolith endpoints are gradually replaced by routing traffic to the new service. The monolith "strangled" over time rather than rewritten.
Should I use gRPC or REST between microservices?
gRPC for synchronous service-to-service calls (typed, efficient binary protocol). Async messaging (Kafka, SQS/SNS) for operations that can tolerate eventual consistency.
What is "team topology" and does it matter?
Team Topologies (Skelton/Pais) describes how team structures shape systems (Conway's Law). Stream-aligned teams owning end-to-end services outperform teams organised by function (front-end team, DB team). It is worth reading before designing a multi-service org.
Where to go next