Software Development Atlas
Engineering JudgmentDecision Guides

Monolith vs Modular Monolith vs Microservices

Choose deployment and domain boundaries by team ownership, transaction needs, failure isolation, scaling pressure, and operational capacity rather than architecture prestige.

EvergreenVerified Sep 9, 2026Review target: 365 days
Edit on GitHub

Personal learning atlas by Tran Trong Thuc · About this Atlas · Atlas last updated Sep 9, 2026

TL;DR

A monolith, modular monolith, and microservices architecture are not three maturity levels. They place code, data, deployment, and ownership boundaries differently.

Start with the least distributed deployment model that meets the real constraints. Improve internal modularity before assuming a network boundary is necessary. Extract a service when independent deployment creates measurable value—for example by removing real release coordination, isolating a workload that must scale separately, creating a deliberately operated failure boundary, or giving one team ownership it cannot achieve inside a shared deployment.

The question is not “Can this be a service?” Ask: which constraint becomes easier after this boundary becomes independently deployed, and how will we measure that improvement?

Decision frame

Write down the forces before choosing an architecture label:

  • How many teams genuinely need independent release schedules?
  • Which business capabilities have stable ownership and different change rates?
  • Which operations benefit from one local ACID transaction?
  • Which workloads need materially different scaling, availability, isolation, or runtime characteristics?
  • Can the organization operate multiple independently failing deployments, data stores, dashboards, alerts, compatibility contracts, and on-call surfaces?
  • Is current pain caused by coordinated deployment, or poor module boundaries inside the codebase?
  • What measurable release-time, runtime, or incident-response outcome would improve if a boundary became a service?

The options

Monolith

A monolith is deployed as one application unit. It can be well modularized or tightly coupled; “monolith” describes deployment shape, not code quality.

Local calls can stay local, transactions can often remain inside one database boundary, and debugging does not inherently cross service networks. The trade-off is that modules share the deployment lifecycle.

Modular monolith

A modular monolith keeps one deployment while enforcing explicit domain boundaries inside it. Modules expose deliberate interfaces, hide implementation details, and can have ownership/dependency rules even though they ship together.

This is useful when teams need stronger code/domain boundaries but coordinated deployment remains acceptable. It also tests whether a proposed boundary is real: if the boundary cannot stay clean inside one process, putting it behind a network API usually preserves the conceptual coupling while adding distributed-system failure modes.

A modular monolith can be a long-lived final architecture.

Microservices

A microservices architecture moves selected domain boundaries into independently deployable services, usually with explicit network APIs/events and separately owned state boundaries.

That deployment independence can matter when teams need separate release schedules, selected workloads need isolated scaling/availability policy, or one capability needs a different runtime lifecycle. The cost is structural: local calls become remote, compatibility spans deployed versions, data consistency can cross transaction boundaries, and observability must correlate work across services.

Monolith vs modular monolith vs microservices decision matrix
CriterionMonolithModular monolithMicroservices
Deployment lifecycleOne application deploymentOne application deployment; modules still ship togetherSelected service boundaries can deploy independently
Domain-boundary enforcementDepends on internal module/dependency disciplineExplicit module interfaces and dependency rules inside one deploymentInternal boundaries plus network/API and deployment contracts
Cross-boundary transaction modelCan remain inside one database/transaction boundary when the design permitsCan remain local across modules that share the transactional storeCross-service workflows usually need explicit distributed consistency/recovery design
Team release autonomyTeams coordinate the shared application releaseCode ownership can be separate, but deployment is still coordinatedA team can release its service independently when ownership and compatibility boundaries are real
Failure boundariesOne process/deployment failure can affect the application unitSame deployment failure boundary; modules can still isolate logic/data errors internallyService failures can be isolated from other processes, while callers must handle partial/ambiguous remote failure
Scaling unitScale the application deploymentScale the application deploymentScale selected services separately when the platform and dependencies support it
Operational workOne deployable/runtime surface, plus application dependenciesOne deployable plus explicit module-boundary governanceOperational work grows with deployable count, networks, data boundaries, compatibility, telemetry, and ownership surfaces

Operational complexity depends on deployment count, infrastructure automation, ownership, data boundaries, reliability requirements, and the maturity of the surrounding platform. The matrix describes mechanisms, not universal low/medium/high scores.

When each model fits

Prefer a monolith when

  • one team or a few closely coordinated teams own the product;
  • the domain is changing quickly and stable service boundaries are not yet evident;
  • coordinated deployment is not causing measurable delivery pain;
  • local transaction/debugging simplicity matters more than independent service lifecycle.

A monolith is not permission to create a ball of mud. Explicit modules, tests, dependency rules, and ownership boundaries still matter.

Prefer a modular monolith when

  • domain boundaries and code ownership need stronger enforcement;
  • teams can coordinate one deployment;
  • you want to reduce coupling before deciding whether independent deployment is necessary;
  • important workflows still benefit from local transaction boundaries.

Prefer microservices when

  • independent deployment solves a concrete, measured coordination problem;
  • service boundaries align with durable business capabilities and clear owning teams;
  • selected workloads require materially different scaling, availability, isolation, or release cadence;
  • the organization can operate routing, telemetry, CI/CD, secrets, version compatibility, incident response, and asynchronous consistency across the intended number of services.

Examples of measurable value include shorter release lead time, reduced coordination with unrelated teams, independently enforced capacity/availability objectives, or a smaller runtime blast radius.

Microservices change the consistency and recovery model

Inside one process, a normal function call completes under one runtime failure boundary. Across a service boundary, the caller can time out without knowing the remote outcome.

Distributed boundaries introduce design work around timeouts, retry budgets, idempotency, partial failure, delivery semantics, tracing/correlation, eventual consistency, compensation, and compatibility between independently deployed versions.

Common traps

Treating microservices as an organizational shortcut

A network boundary does not create a coherent business boundary or clear ownership. If the model is tightly coupled, service APIs can preserve that coupling while making changes and failures harder to coordinate.

Mistaking repository size for deployment pressure

A large codebase may need module boundaries, build tooling, ownership, and dependency rules without requiring many deployables.

Using independent databases as an ideology

Service-owned data can improve autonomy when a service truly owns its business capability. But cross-service workflows must then model consistency and recovery explicitly.

Extracting before observing the pressure

Early service boundaries often encode assumptions rather than durable capabilities. A modular codebase lets you gather evidence about ownership, release cadence, scaling, and failure isolation before committing to a network boundary.

Practical heuristic

Ask these questions in order:

  1. Can better internal module boundaries solve the current problem? Prefer that when deployment independence is not required.
  2. Which exact boundary needs independent lifecycle? Name the release, ownership, scaling, availability, or isolation constraint.
  3. What changes when the call/data crosses a network? Identify timeout, retry, transaction, consistency, and compatibility consequences.
  4. Who operates the new service during an incident? Include dashboards, alerts, dependencies, runbooks, rollback, and ownership.
  5. How will we know the service boundary paid off? Define a measurable outcome before extracting it.

The extraction threshold is reached when independent deployment creates measurable value that a local module boundary cannot provide at lower cost.

Questions to ask before choosing

  • Are the domain boundaries stable enough to defend with interfaces?
  • Is coordinated deployment actually delaying releases or increasing risk?
  • Which transactions would cross a service boundary?
  • What happens when one side succeeds and the other side times out?
  • How will traces/logs correlate one user action across services?
  • Does each service have a durable owning team and incident responsibility?
  • Are independent scaling/availability requirements observed or speculative?
  • Could architecture tests enforce the desired boundary inside one deployment first?

This decision is primarily about Modularity, Domain Boundaries, Coupling and Cohesion, and the point at which Partial Failure becomes worth accepting. The Backend Systems path provides the reliability/data concepts that become essential once boundaries are distributed.

Sources

On this page