SyncTrix logoSyncTrix
All articles
Platform11 min read

Migrating off a monolith without stopping feature delivery

How to decompose a monolith while continuing to ship - choosing seams, sequencing extractions, handling the shared database, and knowing when to stop.

By Aarav Patel
Migrating off a monolith without stopping feature delivery

Monolith decomposition fails in a characteristic way: the team extracts two services, discovers that both still depend on the monolith's database, and stops. What remains is a distributed system with all the operational complexity of microservices and none of the independence, plus a feature backlog that stalled for two quarters. Avoiding that outcome is mostly about choosing seams correctly and being honest about when to stop.

01Decide what problem you are solving

Microservices solve organisational scaling problems - independent deployment by independent teams - at the cost of substantial operational complexity. They do not inherently make software faster, cleaner or more reliable, and a single team running twelve services is usually worse off than the same team running one.

If the actual problem is a slow test suite, tangled code or a scary deploy, those have cheaper solutions: modularise inside the monolith, improve the test pyramid, fix the deployment pipeline. Extract services when independent deployment by separate teams is the binding constraint, not before.

02Choosing seams that will actually hold

The right boundaries follow business capability and data ownership, not technical layering. A service that owns a coherent slice of the domain and its data can evolve independently. A service extracted along a technical line - all the validation, all the notifications - will need coordinated changes forever.

The reliable test is whether the candidate service can own its data outright. If extracting it would leave two services writing to the same tables, the seam is in the wrong place. Move the boundary until data ownership is clean, or pick a different candidate.

  • Follow business capability, not technical layer.
  • Each service owns its data exclusively - no shared writes.
  • Prefer boundaries with low chat across them.
  • Start where team ownership is already clear.
  • Avoid extracting anything still changing rapidly.

03The shared database is the hard part

Most decomposition efforts stall at the database. Extracting application code is comparatively easy; separating data that a decade of joins has entangled is not. Teams that skip this end up with services sharing tables, which is a distributed monolith - worse than what they started with.

The workable sequence is to separate logically before separating physically. Restrict each candidate service to its own tables, replace cross-boundary joins with API calls or replicated read models, and only then split the physical database. Doing it in the other order produces an outage.

StepWhat happensReversible?
Enforce table ownership in codeNo cross-boundary queriesYes
Replace joins with API callsAccept the latency costYes
Introduce read models where neededDenormalised local copiesYes
Split schema, same instanceSeparate schemas, one serverMostly
Split physical databaseTrue independenceExpensive
Database decomposition sequence

04Keeping delivery running throughout

The constraint that makes this hard is that the business will not accept a two-quarter feature freeze, and should not. Migration has to happen alongside delivery, which means each extraction must be small enough to finish inside a normal cycle.

Route traffic through a facade so callers do not change when implementations move. Extract one capability at a time, run both paths in parallel with the new one shadowed first, then shift traffic incrementally with the ability to fall back. If an extraction cannot be completed in a few weeks, it is too big - decompose the decomposition.

  • A facade or gateway so callers are insulated from the move.
  • Shadow traffic to the new path before it serves anyone.
  • Incremental traffic shift with a fast rollback.
  • Each extraction finishes inside one cycle, or it is too large.
  • Delete the old path promptly - dual maintenance is the tax.

05Knowing when to stop

Not every part of a monolith needs extracting, and the last 20% is usually the least valuable. Once the high-change, high-contention areas are independent, the remaining core often serves fine as it is - stable code that rarely changes imposes little cost by staying where it is.

Stopping deliberately is a legitimate outcome, and much better than the common alternative of stopping accidentally at 60% and pretending the programme is still live. Declare the endpoint, delete the transitional scaffolding, and move on.

Topics

monolith to microservices migrationstrangler fig patterndecomposing a monolithmicroservices migration strategydatabase decomposition

Aarav Patel

Principal Engineer · SyncTrix

Writes about the engineering decisions behind production systems - architecture, delivery and the trade-offs that only show up at scale.

Building something like this?

SyncTrix engineers AI, SaaS, platform and cloud systems for enterprises and high-growth teams. Tell us what you're shipping and we'll scope it with you.