Distributed systems

Monolith vs microservices

Distributed systemsArchitectureDecision guide

Short answer

Start with a modular monolith unless independent deployment, scaling, or failure isolation is already a hard requirement. Adopt microservices around stable business boundaries when a team can operate service discovery, observability, retries, data ownership, and deployment complexity without turning every feature into a distributed transaction.

Written and reviewed by Sahil Srivastav

What each one actually is

A monolith deploys multiple capabilities as one process or release, often with one database. A modular monolith still enforces boundaries in code and ownership; it is not permission to share every table and global.

Microservices split capabilities into independently deployable processes with network calls and usually separate data ownership. The split buys autonomy and isolation while making latency, partial failure, and compatibility part of ordinary programming.

The useful comparison is reversible complexity. A well-modularised monolith can be split later; a premature service split creates contracts and operations that are difficult to retract.

Side by side

 MonolithMicroservices
DeploymentOne release coordinates all modulesServices release independently
Failure boundaryA process failure can affect many capabilitiesFailures can be isolated, but calls can fail partially
Data transactionsLocal multi-table transactions are straightforwardCross-service invariants need sagas, outbox, or redesign
LatencyIn-process calls are cheapNetwork hops add latency and timeout paths
ScalingScale the whole application or use internal controlsScale a hot service independently
Team ownershipShared codebase and release coordinationClear ownership, plus contract and platform work
DebuggingOne trace and process are easier to inspectDistributed tracing and correlation IDs are essential
Operational costLower baselineMore deployables, alerts, credentials, and runbooks

Choose Monolith when

  • The product and boundaries are still changing rapidly
  • One team can release and scale the application adequately
  • Business operations need atomic updates across several modules
  • The team does not yet have mature tracing, deployment, and incident practices

Choose Microservices when

  • A capability has a demonstrably different scale or availability requirement
  • Several teams need independent release ownership around stable boundaries
  • A failure or compliance boundary requires process isolation
  • The platform can provide standard discovery, telemetry, deployment, and secrets

The trade-off in detail

A service boundary does not remove coupling; it changes compile-time coupling into runtime coupling. A renamed field becomes a rollout protocol, and an unavailable dependency becomes a user-visible failure. Treat API compatibility and deadlines as part of the feature.

Separate databases improve ownership but make reporting and workflows harder. An outbox plus projections can preserve events, but it introduces lag, replay, and reconciliation work. Budget for those mechanisms before splitting.

A modular monolith is only a credible default if modules have boundaries: private data access, explicit interfaces, and tests that prevent accidental imports. Otherwise the later extraction will be a rewrite.

Things that are commonly said and are wrong

  • “Microservices automatically scale better.” Only the hot service scales independently; coordination and shared dependencies may remain the bottleneck.
  • “A monolith means one giant code file.” Deployment shape and code modularity are separate choices.
  • “Separate databases guarantee independence.” Shared identity, queues, DNS, or a common platform can still couple outages.

Decide it in a real repository

Choosing correctly on a whiteboard and enforcing the choice in code are different skills. Gronex ships broken backend repositories whose tests assert the invariant, not the happy path.

FAQ

When is a monolith too large?

When its deployment, ownership, or scaling constraints are measurably blocking the team. Lines of code alone are not a threshold; a modular monolith can be large and healthy.

Can I split a monolith incrementally?

Yes. Establish a module boundary, move ownership behind an interface, add contract tests, then extract one capability with an anti-corruption layer. Do not begin by sharing tables across the new network boundary.

Do microservices require separate databases?

They should own their data, but the physical arrangement can vary during migration. Shared writes create hidden coupling; make one service authoritative and expose an API or event for consumers.

Other decisions engineers weigh