Architecture

Breaking up a monolith without a rewrite

Incrementally extracting services from a monolithic application

Every monolith migration proposal starts the same way: we'll build the new system alongside the old one, then switch over. Almost none of them end that way. The old system keeps changing because the business keeps needing things, the new system chases a moving target, and eighteen months later you are maintaining two of everything.

The alternative is less satisfying and works far more often: never have a second system. Cut one seam at a time out of the one you have.

First, decide whether to do it at all

Microservices trade a code problem for an operations problem. You get independent deployment and scaling; you pay in network calls, distributed transactions, eventual consistency and a much harder debugging story.

That trade is worth it when you have a specific, named pain:

  • One part of the system needs to scale independently — a report generator that pins CPU while the rest of the app idles.
  • Teams are blocking each other on deploys, and release trains have become a scheduling problem.
  • One component genuinely needs a different runtime — a Python ML service inside a Node application.
  • A single failure mode takes down everything, and you need a blast radius.

The strangler pattern, concretely

Put a routing layer in front of the monolith — a proxy, a gateway, or just a reverse proxy rule. Every request goes through it. At first it forwards everything to the monolith and nothing has changed.

Then extract one capability. The new service handles a narrow set of routes; the router sends those to it and everything else to the monolith. Repeat. The monolith shrinks a little at a time, and at no point is there a big-bang cutover to get wrong.

The key property: you can stop at any point and still have a working system. Half-migrated is a stable state, not a crisis. That is the entire advantage over the rewrite.

Which seam to cut first

Not the most painful one. The first extraction is where you learn your deployment story, your observability story and your inter-service auth story — so pick something forgiving:

  1. Low traffic, so mistakes are cheap.
  2. Few dependencies on the rest of the domain — notifications, PDF generation, image processing, scheduled reports.
  3. Clear input and output, so the contract is obvious.
  4. Ideally asynchronous, so latency and retries are not user-visible.

Notifications are the canonical first service for exactly these reasons. It is dull, and that is the point — extract something dull while you are still learning, not your billing engine.

The hard part is always the data

Splitting code is a week. Splitting a database is the project. The moment two services share a table, you do not have two services — you have one system with extra steps and no transaction.

The sequence that works:

  1. Stop writing to the shared table from the new service. Give it its own store, populated from events or a sync.
  2. Move reads over gradually, comparing old and new results in production before trusting the new path.
  3. Only then remove the old table access. Not before you have run both in parallel long enough to believe the numbers.

Expect eventual consistency and design for it explicitly. If the business genuinely cannot tolerate a few seconds of staleness somewhere, that is a strong signal the seam belongs inside one service, not across two.

What you need before the first extraction

  • Distributed tracing. Without it, a bug that spans two services is unfindable. Add it before you need it.
  • A deploy pipeline that can ship one service without shipping the others — otherwise you have kept every cost and gained no benefit.
  • Contract tests between services, so an internal API change fails in CI instead of in production.
  • An answer to service-to-service auth that is not "we're on the same network".

The version most teams should actually build

For a lot of products the right answer is not microservices at all — it is a well-structured monolith with the two genuinely independent things pulled out: the workload that needs to scale differently, and the workload that needs a different runtime.

Three services that map to real operational boundaries will serve you better than fifteen that map to an org chart. You can always cut another seam later. That is the whole point of doing it this way.

Frequently asked questions

How do you migrate from a monolith to microservices without a rewrite?

Use the strangler pattern: put a routing layer in front of the monolith, then extract one capability at a time into its own service and route just those requests to it. The monolith shrinks incrementally and the system stays working throughout, so there is never a big-bang cutover — and you can stop at any point with a stable result.

Which service should I extract first?

Something dull and forgiving: low traffic, few dependencies, clear inputs and outputs, ideally asynchronous. Notifications, PDF generation or scheduled reports are ideal. The first extraction is where you work out deployment, observability and service auth, so you want mistakes to be cheap — not your billing engine.

Do I actually need microservices?

Only if you have a specific named pain: a component that must scale independently, teams blocking each other on deploys, a component needing a different runtime, or a failure that takes everything down. "Our codebase is messy" is not a reason — distributing tangled code just adds network latency and partial failure to the same problem.

What is the hardest part of splitting a monolith?

The database, without exception. Splitting code takes a week; splitting data is the project. Two services sharing a table is one system with extra steps and no transaction. Give the new service its own store, run both paths in parallel while comparing results, and only then remove the old access.

Keep reading

An application struggling under production load
Scale

Why MVPs Break Under Real Load

Success is the failure mode. The shortcuts that got you to launch are exactly the ones that break when launch works.

Planning the scope and cost of an MVP build
MVP

MVP Development Cost in the UK

Most MVP quotes are wrong for the same reason: they price a feature list instead of a decision. Here are the real UK ranges and what actually moves them.

Comparing a proof of concept, a prototype and an MVP
MVP

MVP vs Prototype vs Proof of Concept

Ordering the wrong one is the most expensive mistake in early product work. Each answers a different question — and only one of them is meant to have users.

Let's put it into production.

Book a 30-minute call — you'll walk away with a scope, a timeline and a fixed price.

Book a call