A monolith is defined by one deployment unit: its modules are versioned, released, and rolled back together. It commonly uses one process and database, but those implementation choices are not the definition. The style keeps operations and in-process transactions simple; its main cost is deployment and scaling coupling as modules and teams grow.

What a Monolith Looks Like

A typical ASP.NET Core monolith can use this layout:

MyApp/
├── Controllers/        # HTTP entry points
├── Services/           # Business logic
├── Repositories/       # Data access (EF Core)
├── Models/             # Domain entities
└── Program.cs          # Single startup, single deployment

One dotnet publish produces one coordinated deployment unit. That unit may run as one process or several collocated processes, and it may use one database or several stores; those parts are still versioned, released, and rolled back together.

Benefits

Operational simplicity: one coordinated release unit reduces deployment and rollback surfaces. It does not eliminate distributed failure: a monolithic deployment that calls external services or spans processes and datastores still needs network-failure handling, tracing, and explicit eventual workflows.

Easy local development: dotnet run starts everything. No service mesh, no container orchestration needed for development.

Simple local transactions: components that share one transactional database can commit together without a distributed protocol. This advantage is common, not inherent: modules using separate resources still need explicit coordination.

Low latency for collocated components: modules in the same process use local calls instead of network hops. Components in another process, datastore, or external service keep their ordinary network latency and failure modes.

NOTE

A monolith still scales horizontally — the common myth is “monolith = can’t scale.” You scale it by running N identical copies behind a load balancer (which is why keeping the process stateless matters). What you can’t do is scale components independently — if only the report generator is hot, you still replicate the whole app. That inefficiency, not a hard ceiling, is the real scaling argument for microservices.

When Monoliths Break Down

SignalWhat it means
Build takes 10+ minutesToo much code in one compilation unit
Deploying one feature requires testing everythingHidden coupling between components
One team’s change breaks another team’s featureShared mutable state, no module boundaries
One component’s load spikes affect all othersNo independent scaling
Database schema changes require coordinating all teamsShared schema ownership

These signals indicate the monolith has become a big ball of mud — not because monoliths are bad, but because module boundaries were not enforced.

Modular Monolith — The Middle Ground

A Modular Monolith keeps one deployment unit while enforcing explicit module contracts and data ownership. It is the normal upgrade path when an unstructured monolith needs stronger change boundaries but independent service deployment is not yet worth the operating cost.

Monolith vs Microservices

AspectMonolithMicroservices
DeploymentSingle unitIndependent per service
TransactionsLocal ACID when components share one resourceLocal transactions plus saga, outbox, or supported distributed coordination across resources
ScalingScale everything togetherScale individual services
Operational complexityLowHigh (service mesh, tracing, retries)
Team autonomyLow (shared codebase)High (independent deployments)
Development speed (early)FastSlow (infrastructure overhead)
Development speed (at scale)Slow (coupling)Fast (independent teams)

See Microservices for the full microservices pattern.

Decision Rule

Start with a monolith (ideally modular). The operational simplicity and development speed advantages are significant in the early stages of a product. Microservices are justified when:

  • Independent deployment is a hard requirement (different release cadences per team).
  • A specific component needs independent scaling (e.g., a video processing service).
  • Teams are large enough that shared codebase coordination becomes the bottleneck.

The cost of premature microservices is high: distributed systems complexity, eventual consistency, and operational overhead before the product has proven its architecture.

Collocation provenance visuals

Prime Video’s monitoring pipeline and Stack Overflow’s historical application tier are provenance cases, not architecture targets. Modular Monolith owns the reusable comparison of collocation, scaling, and boundary decisions.

Pitfalls

Deployment Coupling

What goes wrong: a bug fix in one module requires deploying the entire application. In a 15-team organization with a single monolith deployed via a 45-minute CI/CD pipeline, a one-line CSS fix in the storefront module sat blocked for 3 days because the checkout team’s unrelated database migration kept failing — both changes were in the same deployment artifact.

Why it happens: the monolith is a single deployment unit. Any change to any module triggers a full deployment.

Mitigation: enforce strict module boundaries so that changes in one module do not require touching others. Use feature flags to decouple deployment from release. A modular monolith with clear interfaces reduces the blast radius of any single change.

Database Monolith

What goes wrong: modules share tables or schema ownership without boundaries. A schema change for one module requires coordinating with all teams and risks breaking other modules.

Why it happens: a shared database is a common convenience in a monolith, so table ownership stays implicit as the team grows.

Mitigation: partition the database by module even within a monolith. Each module owns its tables and accesses other modules’ data only through service interfaces, not direct SQL joins. This is the modular monolith approach and is a prerequisite for eventual microservice extraction.

Questions

References

ByteByteGo provenance