More Services Than Engineers Is a Smell
More than one deployable service per engineer? That's usually a smell.
Microservices are a solution to an organizational problem: many teams that need to ship independently without stepping on each other. If you have two engineers and nine services, you have imported that solution without having the problem. You pay the cost of distribution every single day and collect none of the benefit.
What microservices actually buy you
The real payoff of splitting into services is independent deployability and team ownership. Team A ships their service on their schedule; Team B never blocks them; each service scales and fails in isolation. Those benefits only materialize when you have enough people to own the services as separate units, and enough scale that isolating them matters.
For a small team, none of that applies. There is no cross-team coordination to eliminate because there is only one team. So the split buys you nothing and charges you the full price.
The distributed-systems tax
Every network boundary you introduce turns an in-process function call into a distributed systems problem:
- Partial failure. A local call either returns or throws. A network call can hang, time out, or half-succeed. Now you need timeouts, retries, idempotency, and circuit breakers.
- No more transactions. One database transaction across a boundary becomes a saga with compensating actions and eventual consistency.
- Operational surface. Each service needs its own pipeline, deploy, dashboards, alerts, on-call, and versioned contract with its neighbors.
- Debugging. A stack trace becomes a distributed trace across five services and three log systems.
A two-person startup pays all of that and, because there's no independent-ownership problem to solve, banks none of the upside.
Count artifacts against headcount
Use a blunt heuristic: count your independently deployable artifacts and compare to team size.
deployable services > engineers -> investigate
It is not a hard law, but it is a strong smell. If services outnumber engineers, you are almost certainly carrying operational cost with nothing organizational to justify it. The default should be a monolith or a modular monolith: one deployable, clean module boundaries inside it. You keep the design discipline and skip the network. Split a module out into its own service later — when a specific team needs to own it, or a specific component needs to scale or be isolated for real.
| Situation | Better default |
|---|---|
| Small team, one product | Monolith / modular monolith |
| One codebase, clear internal seams | Modular monolith |
| Multiple teams blocking each other on deploys | Split the contended seam into a service |
| One component with wildly different scaling needs | Extract just that component |
Power moves
- Enforce module boundaries inside the monolith (package structure, lint rules, import checks) so a future split is mechanical rather than a rewrite.
- Extract on evidence, not anticipation. Split when a real deploy conflict or scaling ceiling forces it — the seam will be obvious by then.
- Keep one deploy pipeline as long as you can; each new pipeline is recurring operational cost.
- Sam Newman's rule of thumb: don't start with microservices; start with a monolith you can decompose, because you'll understand the domain far better after building it.
Resources
- MonolithFirst — Martin Fowler
- Building Microservices, 2nd ed. — Sam Newman
- Microservice Trade-Offs — Martin Fowler
- Modular Monolith — Simon Brown / "Modular Monoliths" talk
Shipping AI or cloud systems? Yeda AI audits and hardens production LLM and service architectures. Talk to us · Read the blog