
The Monolith vs. Microservices Fallacy
Premature microservices are a complexity tax paid before achieving product-market fit.
The Pendulum Swing
Every five years, the software engineering community undergoes a collective amnesia regarding system architecture. The pendulum swings violently from one extreme to the other, driven by thought leaders at hyperscale companies who possess engineering resources that 99% of the industry does not.
The most damaging of these swings is the premature adoption of microservices.
A narrative has emerged that building a monolithic architecture is an amateur mistake—a sign of technical immaturity. Startups with three engineers and zero customers are designing their initial MVPs with Kubernetes, API gateways, and asynchronous event buses. They are paying the complexity tax of a 1,000-person engineering organization before they have achieved product-market fit.
"You should not adopt microservices to solve a theoretical scaling problem. You adopt microservices to solve an organizational communication problem."
The Distributed Monolith
When a small team attempts to build microservices, they almost never build actual microservices. Instead, they build a distributed monolith.
They split their application into ten distinct repositories, deploy them via separate pipelines, and communicate via network calls. However, because the domains are not properly bounded, a single feature request requires coordinated deployments across five of those services.
They have achieved none of the independence of microservices, but they have inherited all of the latency, observability challenges, and debugging nightmares of distributed systems. A function call that used to take one millisecond in memory now involves serialization, network latency, deserialization, and points of failure.
The Majesty of the Modular Monolith
The correct default for almost every new software system is a modular monolith.
A modular monolith runs as a single process, deployed as a single unit, backed by a single database. However, the internal code is strictly separated by logical boundaries. The billing module does not directly access the user module's database tables; it communicates through well-defined internal interfaces.
This architecture provides the ultimate developer experience. You can refactor across domains with a single IDE command. You do not need to orchestrate distributed transactions. If an error occurs, the stack trace provides the entire context in a single log file.
When to Actually Split
Microservices solve organizational scaling problems, not technical scaling problems.
You should extract a domain into a microservice only when:
- Two distinct teams are stepping on each other's toes deploying the monolith.
- A specific module has wildly different scaling requirements (e.g., an image processing queue vs. an administrative dashboard).
- A specific module requires a different technology stack for performance reasons.
Until one of those three conditions is met, extracting a microservice is an exercise in vanity.
Start simple. Enforce strict boundaries in the code, not in the network. The architecture you need to reach your first million dollars in revenue is not the architecture you need to reach a billion, and pretending otherwise is a recipe for technical bankruptcy.

Kai Cyrus
Founder, Builder, Investor