The verdict
Start with a well-structured, modular monolith. Move to microservices only when you have a concrete reason — independent scaling of specific components, teams that must deploy independently, or fault isolation — and the operational maturity (CI/CD, observability, on-call) to run a distributed system. Microservices trade code complexity for network and operational complexity; adopting them prematurely is one of the most common and expensive architecture mistakes.
How to decide
- 1Name the actual driver: independent scaling, team autonomy, fault isolation — or is it résumé-driven?
- 2Count teams and their deploy needs. One or two teams rarely need microservices.
- 3Do you have the platform to run distributed systems (CI/CD, tracing, metrics, service discovery)?
- 4Can you define clean boundaries from bounded contexts, not technical layers?
- 5Default to a modular monolith. Extract a service when a specific seam causes real, repeated pain.
The options
Monolith
One deployable unit for the whole application.
Best for
- Early products and small teams
- When boundaries aren't yet clear
- Maximizing development speed
Strengths
- Simple to build, test, deploy, and debug
- In-process calls — no network failures or distributed transactions
- One codebase and one deploy to reason about
Weaknesses
- Whole app scales together, even one hot component
- A single deploy pipeline can bottleneck many teams
- Can rot into a big ball of mud without discipline
Modular Monolith
One deployable, but with strict internal module boundaries.
Best for
- Most systems, most of the time
- Keeping the option to extract services later
- Growing teams that aren't yet at microservices scale
Strengths
- Operational simplicity of a monolith with clean seams
- Boundaries you can later extract into services along real seams
- Enforces domain boundaries without the network tax
Weaknesses
- Requires discipline to keep modules from leaking
- Still one deploy and one scaling unit
Microservices
Independently deployable services around business capabilities.
Best for
- Many teams needing independent deploy cadence
- Components with very different scaling profiles
- Fault isolation between capabilities
Strengths
- Independent deploy, scale, and technology choices per service
- Fault isolation — one service failing needn't take down others
- Team autonomy around bounded contexts
Weaknesses
- Network calls fail; distributed transactions are hard (sagas)
- Heavy operational tax: observability, service discovery, CI/CD, on-call
- Debugging spans many services and logs
Trade-offs at a glance
| Dimension | Monolith | Modular Monolith | Microservices |
|---|---|---|---|
| Deployment | One unit | One unit | Independent per service |
| Scaling | Whole app | Whole app | Per service |
| Team autonomy | Low | Medium | High |
| Operational complexity | Low | Low | High |
| Data consistency | Easy (ACID) | Easy (ACID) | Hard (sagas / eventual) |
| Fault isolation | Weak | Weak | Strong |
| Best when | Early / small team | Most systems | Many teams + scale drivers |
In the interview
In the high-level architecture step, or as 'would you use microservices here?' The interviewer is probing whether you reach for microservices reflexively or tie the choice to real drivers and costs.
What a Staff answer includes
A Staff answer defaults to a modular monolith and justifies microservices with a concrete driver plus the operational maturity to run them. It names the distributed-system costs explicitly — network failures, distributed transactions/sagas, observability — and defines boundaries via bounded contexts, not technical layers.
Follow-ups you should expect
- →How would you define service boundaries here?
- →How do you handle a transaction that spans two services?
- →What's the operational cost you're taking on, and are you ready for it?
- →How would you migrate from the monolith to services incrementally?
Common mistakes
- ✕Microservices for a small team with no scaling or autonomy driver.
- ✕A 'distributed monolith' — services so coupled they must deploy together.
- ✕Splitting by technical layer (a 'database service') instead of business capability.
- ✕Underestimating the operational tax: tracing, service discovery, on-call, CI/CD.
Further reading
Related decisions
Synchronous vs Asynchronous Communication
Synchronous when the caller needs an immediate answer to proceed; asynchronous messaging to decouple services, absorb load, tolerate downstream failure, or fan out — at the cost of eventual consistency and more moving parts.
REST vs gRPC vs GraphQL
REST for public, cacheable, resource APIs; gRPC for fast internal service-to-service calls; GraphQL when varied clients need to shape their own queries.