Who should read this
Summary: The equation “microservices = modern, monolith = legacy” collapsed completely by 2026. For teams of 15 or fewer, microservices are a net productivity loss. The safest path is to start with a modular monolith and extract only the parts that need to become services once module boundaries have been validated.
This article is written for CTOs and senior engineers choosing the architecture for a new project.
Core tradeoffs
| Modular monolith | Microservices | |
|---|---|---|
| Deployment unit | Single (1 binary / container) | Independent deploy per service |
| Team autonomy | Code-level module boundaries | Full isolation at the service level |
| Infrastructure complexity | Low (1 CI/CD, 1 DB) | High (pipeline per service) |
| Independent scaling | Not possible (whole-system scale) | Per-service scaling |
| Fault isolation | Risk of in-process propagation | Isolated at service boundaries |
| Team size fit | 1--15 engineers | 15+ |
| Debugging | Single stack trace | Distributed tracing required |
Why startups regret microservices
Microservices move complexity from the codebase into the infrastructure. Code becomes simpler, but you now manage inter-service communication, distributed transactions, service discovery, distributed tracing, and per-service CI/CD.
For a team of 3—5, maintaining that infrastructure means:
- Deploy pipelines multiply with every service. 5 services times individual CI/CD equals 5x the management overhead.
- Debugging is impossible without distributed tracing. Jaeger or Datadog APM becomes a requirement — and another cost center.
- Broken API contracts between services surface at runtime. What would be a compile error in a monolith becomes a production incident in microservices.
Research shows that teams below 10—15 engineers experience a net productivity loss from coordination overhead when adopting microservices.
The modular monolith — the middle path
A modular monolith enforces module boundaries at the code level within a single deployment unit. Each module owns its own domain data and logic, and inter-module communication happens only through defined interfaces (public APIs).
Key rules:
- No direct cross-module DB queries — each module owns its own tables
- Inter-module communication via interfaces only (event bus or service layer)
- Architecture tests (ArchUnit, ArchGuard) automatically detect boundary violations in CI
Shopify runs thousands of engineers on a single Rails monolith using this pattern. When a module matures enough, it gets extracted into a service.
When to move to microservices
Pitfalls to avoid
Further reading
- REST vs GraphQL vs tRPC: 2026 Decision Guide — Choosing the communication style between modules or services
- AWS vs GCP vs Azure: 2026 Startup Cost Comparison — Infrastructure costs on top of the architecture
- Supabase vs PlanetScale vs Neon: Choosing Postgres for SaaS — Monolith or services, you still need to pick a database