When to Split a Monolith
Engineering

When to Split a Monolith

A split is useful only when the new boundary removes more friction than it creates. Evaluate one extraction through a boundary selection exercise, a pricing example and measurable stop rules.

Short answer

Split a monolith when measurements show that one part needs to release at a different pace, scale on its own, or fail without taking other functions down. That part must be the single owner of its data, and the gain must cover the extra network and operating cost. Try the boundary inside the monolith first; if independent releases and safe rollback do not materialise, do not create new services.

What are microservices, and why split a monolithic architecture?

A microservice delivers a specific business capability through an explicit interface and its own deployment. In a monolithic architecture, the main application components run as one deployable unit. A large codebase is not automatically a reason to split it. A small team can operate one application effectively for a long time with clear modules and a reliable release pipeline. Extraction becomes useful when one capability needs a different release rhythm, separate scaling, or a failure boundary that protects other workflows. The boundary should remove more friction than its network and operating costs introduce.

Begin by asking what actually prevents delivery. Slow builds, fragile deployments, conflicting team changes and an unstable external dependency are different problems. Moving missing tests or a manual release process onto separate servers does not repair them. A modular monolith creates enforceable package boundaries and named ownership within one deployment. Establishing that internal boundary first gives you evidence about whether it is coherent before you add remote calls, independent infrastructure and a second set of failure modes.

Describe the constraint through observed behaviour. If one busy component forces you to scale the whole application, measure resource usage by capability. If teams wait for each other's releases, record the reasons and waiting time. Write one sentence connecting the current baseline to an acceptable outcome. Avoid assuming that every slow release or expensive server means the application needs a microservices architecture.

A service boundary selection exercise

The first deliverable is a comparison of candidate boundaries. Draw one business workflow and mark the owners of its decisions, data sets and external contracts. AWS's business-capability decomposition guidance starts from value-producing capabilities such as sales or customer service. Include someone who understands the business rules alongside the developer who knows the code. An attractive package structure does not explain who may change a price, approve an invoice or correct a historical transaction.

Observed situationFirst optionEvidence for extraction
Missing tests, fragile releasesImprove the release pipelineThe boundary must reduce the problem
Shared writes to the same dataEstablish ownership in the monolithIdentify one authoritative writer
Different business release rhythmsTrial an independent boundaryReleases need no coordinated deployment
Load concentrated in one componentMeasure the scaling candidateSavings must cover extra operations
External dependency disrupts every workflowSeparate the failure boundaryFailure must remain contained

Choose three candidates and write a one-page decision record for each: business purpose, data owner, consumers, synchronous calls, shared transactions, expected benefit and added cost. Pricing may be a coherent capability; a generic database access layer used by every feature usually is not. Trial the candidate with manageable dependencies and a measurable benefit. A total score must not hide a blocker such as a financial invariant that still requires a shared transaction.

What should be ready before extracting a service?

Create the proposed boundary inside the monolith first. Other modules should use its interface rather than reach directly into pricing tables. Document request and response fields, mandatory values, errors and compatibility expectations. A pricing request might include the product, customer group, currency and calculation time; its response might contain the total, tax and rule version. Tests capturing existing behaviour with concrete cases reveal unexpected changes while the implementation moves. Define the meaning of every monetary field, including whether tax is included and how rounding is applied.

Resolve data ownership alongside code ownership. Microsoft's data considerations for microservices explains how direct access to shared tables couples services through their schemas. Separate physical database servers are not mandatory; data and schema boundaries are the concern. If changing a service's schema still requires every team to deploy together, the intended deployment autonomy has not arrived. Describe transitional access explicitly and give it an owner and an exit condition.

For each data set, record its authoritative writer, reading interface and freshness requirement. Where order, price and inventory changes currently share a transaction, identify which business rule must remain atomic. A report that tolerates delayed updates and a payment amount that must be correct belong to different consistency decisions. Avoid unexplained writes to two applications. Design failure handling, retries and reconciliation before moving the write path. If the invariant cannot be separated sensibly, a larger boundary or a modular monolith may be the better operating choice.

The service owner must also own deployment, monitoring, security updates and incident response. Prepare a delivery pipeline, authorised service identity, secret management, request identifiers and dependency latency records. Set timeout and retry limits within an end-to-end latency budget. The right response to an unavailable dependency follows the business rule: silently reporting success is unsuitable for a payment that has not been confirmed. Also decide what information support staff need to trace a request across both applications without exposing unnecessary customer data.

Trial one reversible extraction

Start with shadow reads or calculations that have no side effects. The old application remains authoritative while the new implementation produces results only for comparison. Microsoft's Strangler Fig pattern describes replacing functionality incrementally behind a shared entry point. It allows you to investigate one boundary while the rest of the monolith continues serving users. Put routing behind a switch and preserve the old path's capacity throughout the trial.

Compare results produced from the same inputs and rule version. Two quotes calculated against different price data are not a meaningful parity test. Classify differences, approve intended changes and fix unexplained results before exposing users. Then begin with a small cohort chosen for its risk and traffic. A routing switch may be enough to reverse a pure calculation trial. Where the new service writes records, you must also prove that the old application can understand and process them. Write the data rollback plan before moving that ownership.

Hypothetical example: pricing in an order application

Suppose a six-person team releases its order application four times a month, while pricing rules change twice a week. Over the previous six weeks, eight of twelve price changes waited for a main application release, with a median delay of three days. The system calculates 20,000 quotes daily. Pricing is a candidate business capability, and the initial objective is independent price changes while preserving order performance. All these figures are hypothetical; they are not a Hatson customer result or a claim about typical projects.

The team first establishes an internal pricing interface and ownership of the rules. It compares 2,000 historical quotes using identical inputs and records rounding and tax differences separately. After resolving discrepancies, it sends 5% of traffic to the new service. For this example, acceptance means zero unexplained price differences, end-to-end p95 latency at most 800 milliseconds and a price-change wait below one day. These are local business targets rather than universal migration thresholds. The team records the rule version with every comparison so a changing dataset cannot distort the result.

After two releases, waiting time improves but infrastructure and on-call work add four hours per week. The team evaluates that cost alongside the delivery benefit. If each price change still needs a simultaneous order-team release, it pauses further extraction. It revisits the data boundary and may keep pricing as a module in the single application. Independent rollback, stable error rates and a team able to operate the service are part of the result. Counting deployments alone would overlook the new maintenance burden.

A microservices migration test and measurement plan

  1. Record the baseline: Measure blocked deployments, change lead time, failure rate and maintenance hours over the same period. Separate the candidate capability from unrelated application changes.
  2. Exercise the contract: Specify expected responses for missing values, old clients, an unsupported currency, unauthorised requests and incompatible versions. Verify both implementations against those expectations.
  3. Inject failures: Stop the service, delay its response and interrupt its network connection. Observe timeout limits, retries and whether unrelated workflows remain available without creating a growing request queue.
  4. Reconcile the data: Compare amounts and record counts after duplicate requests, concurrent updates and cancellations. Assign an owner to every unexplained difference rather than accepting a successful HTTP response as proof.
  5. Practise rollback: Return traffic to the old route and verify capacity and the readability of new records. Check users with transactions already in progress as well as new requests.
  6. Apply the stop rule: After two releases, pause new extractions if the expected benefit is absent. Change the boundary or reconsider the original constraint before funding another service.

Common mistakes

  • Using lines of code or team size as the sole extraction threshold instead of measuring a business bottleneck.
  • Splitting technical layers and adding a long remote call chain to every user request.
  • Preserving shared database writes while multiplying deployments and expecting independent delivery to emerge.
  • Excluding monitoring, on-call work, security maintenance and reconciliation from the cost estimate.
  • Treating a routing switch as sufficient rollback without testing how the old path handles newly written data.

How Hatson Tech builds this

In our custom software development work, we start by mapping workflows, release waits and data ownership, then define a candidate boundary through contracts and acceptance tests. For the broader choice of intervention, see the application modernisation decision guide; for moving a selected capability, see the Strangler Fig migration plan. A useful pilot produces a measured decision as well as an implementation. Sharing the current bottleneck and one candidate capability gives discovery a concrete scope and makes its success criteria reviewable before further services are added.

Key takeaways

  • Target a measured delivery or operating constraint rather than a service count.
  • Compare three candidate boundaries through ownership, calls and shared transactions.
  • Establish the interface inside the monolith and protect behaviour with contract tests.
  • Measure network, on-call and reconciliation costs alongside delivery independence.
  • Pause extraction and revisit the boundary if benefits are absent after two releases.

Frequently asked questions

What is a microservices architecture?

It organises business capabilities as separately deployable services communicating through explicit interfaces. Independence must extend to data ownership and operating responsibility as well as application code.

Should I choose a monolith or microservices?

Consider the team’s release rhythm, data invariants and measured bottleneck. A modular monolith can meet the need with less operating work; validate any need for independent delivery with a small trial.

Is a modular monolith enough?

It may be enough when unclear ownership or tangled package dependencies are the main problem. If separate scaling or failure isolation remains necessary, evaluate the same boundary as an extraction candidate.

Can services use the same database?

They can share a physical server, but shared access to the same tables makes independent change harder. Name the data owner and consumer contract, and give any transitional shared access a clear exit condition.

How do I know a monolith split has succeeded?

Release waiting time or resource cost should improve against the baseline while errors remain acceptable. Measure rollback independence and maintenance work too; a working new service alone does not demonstrate a useful result.

All articles