Monolith is not an anti-pattern, it is a strategy
TLDR;
- Monoliths can be utilized in the right circumstances to improve team performance
- When done properly can improve the DORA Metrics and Time to Production for new projects
- Microservices come with significantly heavier distribution costs for smaller projects
- Good times for use are for experimental projects and startups
Summary
The industry has created a false narrative where monoliths equal bad and microservices equal good. This oversimplified view has caused teams to struggle with unnecessary complexity when simpler solutions would serve them better.
The term "anti-pattern" gets thrown around loosely in our field, often applied to anything that doesn't align with current trends. True anti-patterns are solutions that look helpful on the surface but create more problems than they solve. Monolithic architecture, when done thoughtfully, doesn't fit this definition.
What Monoliths Actually Are
The real issue is the conflation of well-designed monoliths with sprawling, tightly coupled codebases that nobody wants to touch. Those "big balls of mud" aren't representative of what a monolith can be. A well-structured monolith provides clear boundaries between components, remains testable at every level, and preserves the ability to extract portions into separate services when business needs justify it.
Performance Metrics Don't Favor Any Architecture
Research on high-performing teams and organizations has identified key metrics that matter for continuous delivery and team effectiveness. These metrics don't inherently favor one architectural approach over another.
The metrics that drive performance are:
- Mean Time to Recovery (MTTR)
- Lead Time to Production
- Feature Failure Rate
- Deployment Frequency
A well-structured monolith can excel in all these areas. The key difference lies in understanding what "well-structured" actually means.
A good monolith provides clear boundaries between components and remains testable at every level. Teams get the simplicity of a single deployment artifact with straightforward rollback procedures while maintaining autonomy for feature development. Most importantly, the architecture preserves the ability to extract portions into separate services when business needs justify the complexity.
The Strategic Advantages of Monoliths
The Hidden Costs of Distribution
Teams often overlook the cognitive overhead of distributed systems. Debugging at 2 AM becomes significantly simpler with one application to deploy, one set of logs to monitor, and one database to backup. This simplicity translates to faster incident resolution and genuinely lower operational overhead.
Consider the mental load on operations teams when tracing a request through dozens of microservices versus debugging within a single application boundary. Teams regularly burn hours on straightforward issues simply because the complexity spreads across too many moving pieces.
Consider a standard project with a frontend, backend API, and design library used only for this project. Structured as a monolith in a single repository with three folders, every change follows the same pattern:
Make Changes → Validate → Open Pull Request → Merge → Release
When each component becomes its own service with its own repository, the simple pattern above only works for the frontend. For API changes, especially breaking ones, the process becomes:
(Update API) Version Changes, Deprecate Old → Validate → Open Pull Request → Merge → Release
(Update Frontend) Make Changes → Validate → Open Pull Request → Merge → Release
(Update API) Remove Deprecated Code → Validate → Open Pull Request → Merge → Release
That final step reveals the hidden cost. Removing deprecated code provides no immediate value to the consumer, but becomes technical debt that someone must manage. Distribution creates additional operational overhead without corresponding business value.
Domain Boundaries and the Right Abstractions
Extracting services requires a deep understanding of business value, not just code organization. Teams often conflate "putting all the API endpoints together" with "putting the authentication pieces together for multiple systems." These represent fundamentally different value propositions.
Timing matters significantly. The decision to break out portions of a monolith should be driven by what multiple teams actually use and what justifies the extra overhead. Teams that start with microservices from day one lack this domain knowledge, so they default to breaking things out by code structure rather than business domain. This approach typically creates unnecessary overhead without corresponding business value.
Speed When It Matters Most
In the early stages of a project, monoliths offer development speed that's difficult to match. Teams can iterate quickly, refactor boldly, and pivot without the overhead of managing multiple services. There's no network latency to debug, no service discovery to configure, and no distributed transaction complexities to navigate.
This advantage becomes critical during the startup phase of projects or businesses. The time between having an idea and getting it in front of users can determine success or failure. Promising concepts often fail not because they lack merit, but because the architecture makes iteration too slow.
The complexity difference becomes stark in real-world scenarios. Circuit breakers, retries, fallback mechanisms — these aren't just implementation details, they're cognitive overhead that teams carry every day.
When Monoliths Align With Reality
Early Discovery Phase
Monoliths work particularly well for startups and new product development. Teams are still figuring out domain boundaries, and extracting services too early often leads to interfaces that don't make sense and complexity that serves no true purpose.
Acknowledging uncertainty becomes a strategic advantage. Premature optimization in architecture can be just as problematic as it is in code.
Team Size and Communication
Conway's Law provides a useful framework here. Organizations design systems that mirror their communication structure. Small, cohesive teams naturally align well with monolithic architectures.
With a monolith, team communication overhead follows a predictable pattern. Multiple services introduce complexity that often exceeds the team's ability to handle it effectively.
# Team communication overhead
# Monolith team: n people = n(n-1)/2 communication paths
# Microservices: n services × m people per service = exponential complexity
Living With Uncertainty
When domain boundaries remain unclear, extracting services prematurely creates artificial seams in the architecture. Splitting a well-designed monolith proves significantly easier than merging poorly designed microservices.
The cost of getting service boundaries wrong exceeds the cost of refactoring within a monolith by orders of magnitude. This asymmetry becomes critical when making architectural decisions under uncertainty.
Understanding the Microservices Trade-off
Microservices aren't inherently superior — they represent a different set of trade-offs that work well in specific contexts. They excel when organizations have large, autonomous teams that need to work independently, well-understood domain boundaries that are unlikely to change, diverse technology requirements across different parts of the system, and genuinely independent scaling needs for different components.
These benefits come with real costs. Distributed system complexity, operational overhead, and potential performance penalties aren't theoretical concerns — they become daily realities that teams must manage.
Growing Into Complexity
Starting with a monolith provides a clear evolutionary path. As systems and teams grow, services can be extracted strategically, armed with real-world usage patterns and deep understanding of domain boundaries that couldn't exist at the beginning.
graph LR
A[Monolith] --> B[Modular Monolith]
B --> C[Service-Oriented Architecture]
C --> D[Microservices]
This evolutionary approach reduces risk practically. Service boundaries end up reflecting actual business needs rather than early architectural assumptions.
Choosing What Serves You
Monolithic architecture isn't a stepping stone to microservices — it's a legitimate architectural choice that can serve applications throughout their entire lifecycle. The key lies in building a well-structured monolith with clear boundaries and good separation of concerns.
Before defaulting to microservices, teams should ask honest questions. Do you have the team size, operational maturity, and well-understood domain boundaries to justify the additional complexity? If not, there's real value in embracing the monolith as a strategic choice rather than a temporary compromise.
The goal isn't choosing the most fashionable architecture. It's choosing the architecture that best serves users, teams, and business objectives. Sometimes, that's a monolith, and that's perfectly valid.