Why You Probably Don't Need Microservices
A pragmatic look at when a monolith is the right choice and how to avoid distributed-system complexity you haven't earned.
Most teams that adopt microservices do it too early. The appeal is obvious - independent deployability, technology diversity, team autonomy and preparing architecture to be scalable from the start - but those benefits come with a steep operational tax that many organisations are not ready to pay. And some products - just don’t grow so much they finally need the microservices architecture.
The Monolith Is Not the Enemy
A well-structured monolith with clear module boundaries is usually easier to develop, deploy and test than a distributed system of comparable scope. You get:
- Simple local development - one repo, one build, one process.
- Straightforward debugging - stack traces that don’t cross network boundaries.
- Atomic deployments - no choreographed rollouts or version-compatibility matrices.
- Lower infrastructure cost - one load balancer, one database, fewer moving parts.
When Microservices Make Sense
Microservices solve organisational scaling problems, not technical ones. Consider them when:
- Multiple autonomous teams need to deploy independently on different cadences.
- Parts of the system have genuinely different scaling profiles (e.g., an image-processing pipeline vs. a CRUD API).
- You have the operational maturity to run distributed tracing, centralised logging, service meshes and automated canary deployments.
If none of those apply, you are adding accidental complexity.
The Hidden Costs
Teams that split too early typically discover these costs the hard way:
- Distributed transactions - compensating actions, eventual consistency, all harder to reason about than a database transaction.
- Network reliability - timeouts, retries, circuit breakers, idempotency keys.
- Observability - correlating logs across ten services requires tooling you didn’t need before.
- Testing - integration tests become slow, flaky and expensive; contract testing adds process overhead.
- Deployment coordination - “independent deployability” often degrades into “we deploy everything together on Thursdays.”
# Typical service dependency nightmare
services:
order-service:
depends_on: [inventory-service, pricing-service, user-service]
inventory-service:
depends_on: [warehouse-service, supplier-service]
pricing-service:
depends_on: [promotion-service, user-service]
# Running this locally? Not so easy...
A Better Path
Before reaching for microservices, invest in:
- Modular monolith architecture - enforce module boundaries with clear interfaces and encapsulated data access. If built properly, they are easy to maintain in that stage, and relatively easy to split out to microservice, if they need to eventually be.
- Continuous deployment - if you can deploy your monolith multiple times a day without big overhead, the “deploy independently” argument weakens.
- Vertical slices - organise code by business capability, not by technical layer.
Conclusion
Using microservices is not a bad pattern, it just might not be the “go to” solution for most of the projects, especially at the beginning stage. Start with the simplest architecture that meets your current goals, but invest in clean boundaries and split them only when you reach the stage when it starts to be constraint. And before it happens, enjoy easier debugging, local development and lack of operational complexity.