Last Updated: January 8, 2026
In the previous chapter, we explored client-server architecture and its various tiers. We established that the server side handles business logic, data access, and coordination.
But how should we organize all that server-side code? The oldest and most straightforward answer is the monolithic architecture.
A monolith is a single, unified application where all functionality lives in one codebase, runs as one process, and is deployed as one unit.
The user authentication code, the order processing code, the payment handling code, and the notification system all live together, compiled and deployed as a single artifact.
Despite the recent industry excitement around microservices, monoliths remain the right choice for many applications. They are simpler to develop, simpler to deploy, and simpler to operate. Most successful companies started with monoliths, and many continue to run them at scale.
In this chapter, you will learn:
A monolithic application has three defining characteristics:
All the code for the application lives in one repository. Different features are organized as modules, packages, or directories, but they all compile together.
The entire application runs as one process. All modules share the same memory space, the same thread pool, and the same runtime environment.
The application is packaged and deployed as one artifact. Whether it is a WAR file, a Docker container, or a binary executable, there is one thing to deploy.
This stands in contrast to distributed architectures where different components have separate codebases, run as separate processes, and are deployed independently.
While a monolith is a single application, it still has internal structure. Good monoliths are organized into layers and modules.
The most common internal structure is horizontal layers:
| Layer | Responsibility | Examples |
|---|---|---|
| Presentation | Handle HTTP requests, render responses | Controllers, REST endpoints, views |
| Business | Execute business logic and rules | Services, domain objects, validators |
| Data Access | Persist and retrieve data | Repositories, ORMs, database queries |
Each layer depends only on the layer below it. The presentation layer calls the business layer. The business layer calls the data access layer. This creates a clean separation of concerns.
In addition to horizontal layers, good monoliths have vertical modules organized by business domain:
This modular monolith approach gives you the organizational benefits of separation while keeping the operational simplicity of a single deployment.
Understanding the request lifecycle in a monolith clarifies its simplicity:
Key observations:
This simplicity is a monolith's greatest strength. There is no distributed systems complexity, no network partitions between components, no eventual consistency to manage.
Developers can run the entire application locally. There is no need to coordinate multiple services, set up service discovery, or manage inter-service communication.
Deployment is straightforward: build one artifact, deploy it to servers. No complex orchestration, no service dependencies to coordinate, no distributed versioning headaches.
When something goes wrong, you have one log file, one process to inspect, one stack trace that shows the entire call chain:
Compare this to distributed systems where you need distributed tracing, correlation IDs, and log aggregation just to reconstruct what happened.
Database transactions work naturally:
In a distributed system, ensuring this kind of atomicity requires complex patterns like sagas or two-phase commit.
In-process calls are orders of magnitude faster than network calls:
| Call Type | Typical Latency |
|---|---|
| In-process method call | Nanoseconds |
| Local network (same machine) | ~0.5 ms |
| Cross-datacenter network | 10-100+ ms |
A monolith avoids the serialization, network transmission, and deserialization overhead of distributed systems.
One application means:
The operational complexity is dramatically lower than managing dozens of services.
As applications grow, the monolith's simplicity can become a liability.
You can only scale the entire application together:
If the payment module needs more capacity but the user module does not, you still scale everything. This wastes resources.
Every change requires redeploying the entire application:
A small change in one module means testing and deploying modules that did not change. This increases risk and slows down releases.
The entire application uses one technology stack:
You cannot choose the best tool for each problem. You must use what the monolith uses.
As teams grow, everyone works in the same codebase:
Merge conflicts become common. Changes in shared code affect multiple teams. Deployment coordination becomes a bottleneck.
Large monoliths take a long time to start:
A bug in one module can crash the entire application:
As monoliths age, they can fall into a destructive pattern:
Each stage makes the next worse:
This is not inevitable, but it requires discipline to avoid. Clear module boundaries, consistent refactoring, and strong code review practices help prevent the spiral.
Even in a monolith, you can enforce separation between modules:
Techniques:
Structure the monolith around business domains:
Each bounded context has its own domain model and can evolve independently. Communication between contexts happens through well-defined interfaces.
Even with a shared database, separate tables by domain:
Modules should not directly query other modules' tables. This prepares you for future separation if needed.
Allocate time for ongoing improvement:
Small, continuous improvements prevent the death spiral better than occasional big rewrites.
| Indicator | Why Monolith Works |
|---|---|
| New project | Start simple, evolve as needs become clear |
| Small team | < 10 developers, coordination overhead minimal |
| Unclear domain | Boundaries will change, easier to refactor one codebase |
| Transactional requirements | ACID transactions across features |
| Quick time to market | Fewer moving parts, faster initial development |
| Limited DevOps capacity | One thing to deploy and monitor |
| Sign | What It Means |
|---|---|
| Merge conflicts daily | Too many people in one codebase |
| Deploys take hours | Build and test cycle too long |
| Changes cause unrelated breakage | Poor module boundaries |
| Cannot hire for new tech | Stack is outdated, cannot modernize parts |
| One module needs different scaling | Resource needs diverging |
Do not assume microservices are always better. A well-designed monolith is often preferable to poorly implemented microservices. The question is whether the team and system have outgrown what a monolith can handle.
Stack Overflow runs on a monolithic ASP.NET application. As of recent reports, it serves millions of users with just a handful of servers. They achieved this through careful optimization rather than distribution.
Shopify's main application is a large Ruby on Rails monolith that has been running for over a decade. They invest heavily in modularization within the monolith rather than splitting into services.
Basecamp explicitly advocates for monolithic architecture. They run their products on Rails monoliths, arguing that the simplicity benefits outweigh the scaling limitations for most applications.
These examples show that monoliths can scale to significant size when well-maintained and when the scaling requirements allow it.
Monolithic architecture keeps all application components together in a single deployable unit:
Monoliths are not inherently bad. They are the right choice for many applications. The key is recognizing when you are outgrowing the monolith model: when teams step on each other, when deployment becomes risky, when parts need independent scaling.
When these pain points become significant, you might consider breaking the monolith into smaller, independently deployable pieces.
This is the promise of microservices architecture. But microservices come with their own complexity. In the next chapter, we will explore what microservices really are, how they work, and what problems they introduce while solving others.