intermediate 2 min answer

A product team of fifteen engineers must choose between a modular monolith and services. What should decide it?

loommodular-monolithboundariesteam-sizeevolution
Show the full answer Hide the answer

What should decide it

Whether any component has a genuinely different operational requirement, not team size or domain complexity.

For a product with a video-processing pipeline the answer is usually yes — transcoding is CPU-intensive, bursty, long-running and failure-tolerant, which shares nothing with serving an API. That component is a legitimate service.

Everything else — the API, the workspace model, sharing, permissions, comments, search — likely has the same operational profile and belongs in one deployable with strong module boundaries.

What the modular monolith preserves

  • The design discipline: enforced dependency rules between modules, checked in the pipeline, so boundaries are real rather than aspirational.
  • The ability to test a module in isolation, which is most of the practical benefit of a service boundary.
  • One transaction boundary, which removes an entire class of distributed-consistency problems from the parts of the domain that share invariants.
  • One deployment, one debugging session, one on-call surface for a team of fifteen that cannot support many.

What it gives up

Independent deployability and independent scaling for the modules inside it. For a team of fifteen, those are usually not needed — fifteen engineers do not have enough independent workstreams to be blocked by a shared pipeline, and the components rarely have divergent scaling profiles.

The evolution property that makes it the better starting point

A well-bounded module is the cheapest possible thing to extract into a service later. The boundary already exists, the dependencies are already enforced, and the extraction is a deployment change rather than a design project.

The reverse is not true: a distributed system built before the boundaries were understood has boundaries in the wrong places, and moving a boundary across a network is far harder than moving one inside a process.

Start with the monolith and extract on evidence — a component with a genuinely different scaling need, a different failure domain, or a team that is genuinely blocked. Each extraction then has a reason that can be stated.