← All posts
7 min read

Monoliths and Microservices: Choosing Deployment and Ownership Boundaries

#technology#software-architecture#monoliths#microservices
📑 On this page

Software architecture discussions sometimes treat a monolith as an old mistake and microservices as an automatic upgrade.

That framing ignores the trade.

A monolith packages multiple capabilities into one deployable application; microservices package capabilities into independently deployed networked services.

Microservices can improve independent ownership and scaling. They also replace in-process complexity with distributed-system and operational complexity.

A concrete example: a small online shop

A five-person team operates:

  • Product catalog
  • Cart
  • Checkout
  • Customer accounts
  • Order history

One well-structured application may let the team:

  • Change related features together
  • Run tests locally
  • Use one transaction for an order
  • Deploy one artifact
  • Trace one process

Splitting this into twenty services gives the team twenty deployments, service identities, dashboards, versioned contracts, and failure paths.

The smaller code repositories do not reduce total system complexity.

What a monolith is

A monolith is one deployable unit.

It may still contain:

  • Modules
  • Layers
  • Internal APIs
  • Background jobs
  • Several user interfaces
  • One or more databases

Monolith does not mean one giant file or no architecture.

A modular monolith preserves strong internal boundaries while deploying together.

Many problems blamed on monoliths are actually problems of poor modularity, unclear ownership, or unsafe deployment practices.

Benefits of a monolith

A monolith often provides:

  • Simple local development
  • Direct function calls
  • Straightforward transactions
  • One deployment pipeline
  • Easier end-to-end refactoring
  • Fewer network failures
  • Centralized observability
  • Lower infrastructure cost

For a small or rapidly learning product, these advantages are substantial.

One process can still scale horizontally behind a load balancer, and modules can preserve future extraction options.

Limits of a monolith

As a monolith and organization grow:

  • Builds and tests may slow.
  • One deployment carries many changes.
  • Teams coordinate on shared code.
  • A memory leak can affect the whole process.
  • One workload may force scaling everything.
  • Technology choices become shared.
  • Ownership can blur.

These pressures are not guaranteed.

Good tooling, module boundaries, test selection, and deployment automation can extend the useful life of a monolith considerably.

Measure the actual bottleneck before splitting.

What a microservice is

A microservice is an independently deployable service aligned to a business capability or bounded responsibility.

It should normally own:

  • Its code
  • Its interface
  • Its operational lifecycle
  • Its relevant data decisions
  • A team accountable for it

The word micro does not specify line count.

A service that is too small may create chatty calls and fragmented ownership. A service that is too broad may preserve the same coordination problem behind an HTTP boundary.

Benefits of microservices

Microservices can provide:

  • Independent deployment
  • Independent scaling
  • Clear team ownership
  • Fault isolation
  • Technology choice within boundaries
  • Smaller code context per service
  • Security isolation for sensitive capabilities

A video-processing service may scale GPU workers without scaling account management.

A payment service can have stricter access and release controls.

These benefits appear only when the boundaries and operational practices support them.

Network calls replace function calls

An in-process call:

total = calculateTotal(cart)

is fast and usually succeeds or fails immediately.

A service call can:

  • Time out
  • Succeed after the caller times out
  • Return a partial error
  • Be retried
  • Encounter incompatible versions
  • Lose network connectivity

Every distributed boundary needs:

  • Timeout
  • Retry policy
  • Idempotency
  • Authentication
  • Serialization
  • Monitoring
  • Failure behavior

This is the distributed-system tax.

Data ownership is the difficult boundary

If all services directly modify one shared database, they remain tightly coupled through schema and transactions.

Independent ownership often means each service controls its data and exposes behavior through APIs or events.

This creates challenges:

  • Cross-service reporting
  • Eventual consistency
  • Duplicate data
  • Distributed workflows
  • Data migration

Do not split code into services while leaving ambiguous data ownership.

Data boundaries are often harder than deployment boundaries.

Transactions become workflows

Inside one database transaction, checkout can atomically create an order and reserve inventory.

Across services, one global transaction may be unavailable or undesirable.

The workflow may:

  1. Create pending order.
  2. Reserve inventory.
  3. Request payment.
  4. Confirm order.
  5. Compensate when a step fails.

Users can observe intermediate states.

The design needs idempotency, durable state, retries, and reconciliation.

Microservices make business failure paths explicit, but the implementation is more complex.

Independent deployment requires compatibility

Service A and Service B will not always deploy simultaneously.

Interfaces need:

  • Backward-compatible changes
  • Versioning policy
  • Consumer-aware testing
  • Deprecation periods
  • Tolerant readers

Database migrations may use expand-and-contract:

  1. Add new structure compatibly.
  2. Deploy code that uses both forms.
  3. Migrate data.
  4. Remove old structure after all consumers change.

Independent deployment is an engineering capability, not merely separate repositories.

Operational maturity

Microservices multiply:

  • Deployments
  • Logs
  • Metrics
  • Traces
  • Alerts
  • Certificates
  • Secrets
  • On-call ownership
  • Dependency graphs

Teams need:

  • Automated delivery
  • Service templates
  • Central observability
  • Reliable infrastructure
  • Incident practices
  • Cost visibility
  • Clear ownership

Without this platform and culture, service autonomy becomes operational fragmentation.

Testing changes

A monolith can run broad tests in one process and database.

Microservices need a layered strategy:

  • Unit tests
  • Component tests
  • Contract tests
  • Integration tests
  • Focused end-to-end tests
  • Production monitoring

Testing every combination of live services is expensive and unstable.

Contract tests verify interface assumptions while each service remains independently testable.

End-to-end tests still matter for critical journeys, but cannot carry the entire confidence burden.

Fault isolation is not automatic

Separate processes can limit memory or crash propagation.

But a failing service can still trigger:

  • Retry storms
  • Thread exhaustion
  • Queue buildup
  • Cascading timeouts
  • Shared database overload

Use timeouts, concurrency limits, circuit breakers, bulkheads, queues, and graceful degradation.

A network boundary creates the opportunity for isolation, not the guarantee.

Organizational alignment

Microservices work best when a team can own a service end to end:

  • Development
  • Deployment
  • Operations
  • Reliability
  • Roadmap

If every change still needs approval from several centralized teams, the service boundary may add handoffs rather than autonomy.

Conversely, a large monolith shared by dozens of teams can make coordination expensive.

Architecture and organization shape one another.

The distributed monolith

A distributed monolith has many services but must deploy or change them together.

Signs include:

  • Shared database tables
  • Synchronous call chains
  • Breaking internal APIs
  • One release train
  • No independent ownership
  • Local development requiring every service

It combines microservice operational cost with monolithic coupling.

The remedy may be stronger service boundaries, fewer services, or rejoining components that do not benefit from separation.

When to extract a service

Evidence for extraction includes:

  • One capability needs independent scaling.
  • A distinct team owns it.
  • It has a clear data boundary.
  • Security requires isolation.
  • Its release rate conflicts with the rest.
  • Failure should be contained.
  • A stable interface already exists.

Extraction has migration cost.

Start with a narrow boundary, preserve compatibility, move ownership deliberately, and monitor the new failure modes.

Do not begin with the easiest code to move if it creates a meaningless service.

Knowledge check

  1. Why is a monolith not necessarily poorly structured?
  2. What new concerns appear when a function call becomes a network call?
  3. Why is data ownership central to microservice independence?
  4. What is a distributed monolith?
  5. Which evidence can justify extracting a service?

The one idea to remember

Monoliths simplify local change and consistency; microservices enable independent deployment, scaling, and ownership by accepting network, data, and operational complexity. Choose the boundary that fits the real organization and problem.