The Case for Monoliths
Microservices dominate conference talks, but monoliths dominate production systems. For teams under 50 engineers, a well-structured monolith often delivers faster development, simpler operations, and lower cognitive load than distributed systems.
The microservices hype cycle peaked around 2018. Every conference talked about breaking monoliths into services. The reality: most teams that adopted microservices early have regretted it. They traded a monolith for a distributed monolith.
Modular Structure
The key is modularity: clear internal boundaries, explicit interfaces between modules, and independent deployment units within the monolith. Each module owns its domain logic, data access, and API. The monolith is a deployment boundary, not a code boundary.
The module boundary is enforced by the build system. Each module is a separate compilation unit. Cross-module dependencies are explicit and versioned. Circular dependencies are prohibited. This creates a dependency graph that mirrors the domain model.
Extracting When Ready
When a module grows too large, has independent scaling requirements, or needs separate team ownership, extract it into a service. The modular structure makes extraction straightforward because boundaries already exist.
Our Recommendation
Start with a modular monolith. Extract services only when clear boundaries and independent scaling requirements emerge. This avoids premature complexity while preserving future flexibility.
The organisations that succeed start simple and evolve complexity. They build a modular monolith, grow it until boundaries emerge, and extract services when the benefits justify the costs. The organisations that fail start with microservices, create complexity prematurely, and spend years paying off the technical debt.