A startup with 20 engineers and steady moderate traffic proposes moving to microservices to prepare for growth. What would you recommend and under what conditions would you change your mind?
Show the full answer Hide the answer
What is being tested
Whether you can resist a fashionable answer, and whether you understand that module boundaries and process boundaries are separable decisions.
The recommendation
Build a modular monolith: one deployable, enforced internal module boundaries, each module owning its own tables with no cross-module joins, and boundary violations failing the build rather than being caught in review.
This gives the startup the thing they actually want — boundaries that allow the system to be understood and later split — without the thing they do not need: a network between every component.
Why microservices are the wrong purchase at this size
The overhead is fixed and the benefit is proportional to team count. Service discovery, distributed tracing, per-service CI/CD, contract testing, deployment orchestration, on-call rotations per service, cross-service schema coordination — that cost is roughly the same whether you have 20 engineers or 200. With 20, it consumes a large fraction of the team's capacity for no return, because 20 engineers do not have a deployment coordination problem.
You do not yet know where the boundaries go. The most expensive mistake in a distributed system is a boundary in the wrong place, because moving it requires a data migration and a coordinated release across teams. In a monolith, moving a boundary is a refactor the compiler helps you with. Deciding boundaries early — when you understand the domain least — and making them maximally expensive to change is precisely backwards.
Transactions stop being transactions. Business operations that are currently one atomic database transaction become sagas with compensating actions, and every one of those is new code, new failure modes, and new correctness risk.
Shopify is the useful existence proof: one of the highest-traffic commerce platforms in the world, subject to flash sales that spike by orders of magnitude within seconds, running a deliberately maintained modular monolith. Their scaling unit is the data partition — self-contained "pods" — rather than the service, and that option is only available because the application stayed one deployable.
What would change my mind
Specific forcing reasons, each of which justifies extracting one component rather than adopting a style:
| Signal | Extract |
|---|---|
| A component needs GPUs or 20x the memory of the rest | That component only |
| A component must meet a different availability class | That component only |
| A component sits in a different compliance or data-residency boundary | That component only |
| A workload's traffic profile is wildly different (bursty batch vs steady API) | That component only |
| A team genuinely cannot share a release train — different regulator or contract | That team's surface |
And a general one: when the number of engineers reaches the point where a shared release train is the binding constraint on throughput. That number depends on tooling quality, but it is generally much higher than 20.
What to build now that makes the split cheap later
Enforced module boundaries in CI. No cross-module database access. Explicit interfaces between modules. Asynchronous work already on a queue. Idempotent handlers. Those are the properties that make an extraction a week rather than a quarter, and every one of them is cheap to add today.