Monolith vs Microservices
The monolith-vs-microservices question is one of the most consequential architectural decisions a team can make — and one of the most frequently made wrong. Microservices are often chosen for the wrong reasons (fashion, perceived scalability, "big companies do it") and monoliths are dismissed for the wrong reasons ("not scalable," "old-fashioned"). This page is a direct comparison: what each is genuinely good at, what each makes hard, the modular monolith middle ground most teams should actually build, and the signals that tell you a migration to services is warranted.
Side-by-side comparison
Concern | Monolith | Microservices |
|---|---|---|
Development speed (early) | Fast — one repo, one deploy, no network calls | Slow — infra setup, service contracts, local orchestration |
Deployment | One artifact; all-or-nothing | Per-service; independent rollout and rollback |
Scaling | Scale everything together | Scale the bottleneck service only |
Data consistency | ACID transactions across all data | Eventual consistency; distributed transactions are hard |
Debugging / tracing | One log, one stack trace | Distributed traces across services and log streams |
Testing | Straightforward integration tests | Must spin up many services; contract tests needed |
Operational overhead | One pipeline, one runtime, one monitoring target | N pipelines, N runtimes, N dashboards |
Team autonomy | Changes require coordination | Teams own services end-to-end |
The modular monolith — the best of both
MODULAR MONOLITH: one deployable unit, strict internal boundaries
src/
modules/
users/ ← its own service layer, repository, types
index.ts ← public API: what other modules MAY call
orders/
index.ts ← public API
billing/
index.ts
shared/ ← genuinely cross-cutting: config, logging, errors
Rules:
✅ Module A calls Module B ONLY through B's public index.ts
❌ Module A never imports from B's internal files directly
❌ Module A never queries B's database tables directlyWhen microservices are actually warranted
Genuine organizational scale — multiple teams working in the same codebase are slowing each other down with merge conflicts, coordination overhead, and deployment coupling.
Bottleneck isolation — one specific component (search, video processing, recommendations) needs to scale or be replaced independently of the rest.
Technology mismatch — a capability genuinely needs a different runtime (Python for ML, Go for high-throughput, Rust for systems work) that can't share the Node process.
Compliance isolation — a component handling PCI or PHI data must be isolated into its own security boundary.
Team autonomy is a hard requirement — org-level decision that teams must deploy independently; Conway's Law means the architecture follows org structure.