Skip to main content
Monolith vs Microserviceslesson 4 of 4 · 3 min read

How to Actually Migrate

The strangler fig

When a split really is justified, how you do it matters as much as the decision to do it.

Use the pattern with the best track record, sometimes called the strangler fig. Put a routing layer in front of your monolith, carve off one capability at a time into a new service, send that traffic to it, and let the monolith shrink until what remains is gone or small enough not to matter.

Notice the property that makes it work: at every single point during the migration you have a working system in production.

Choose your first extraction strategically. Good candidates sit at a natural seam, with few callers, a clear data boundary, and ideally a real pain like needing to scale differently. Sending notifications, processing images and building search indexes turn up early in most migrations for exactly those reasons.

Avoid the tangled heart of your domain, like orders, as a first candidate. You would be learning to operate distributed systems on your highest-risk component.

The data is the hard part

Plan the data explicitly, because it is the hard part. A common sequence has your new service write to its own store while the monolith keeps writing to the old tables, with change events keeping the two aligned.

Move the reads next, first behind a shadow that compares the new path against the old, then for real. Only once the monolith stops touching those tables do you delete them.

Skip steps there and you land in the distributed monolith from the last lesson.

Set expectations with your leadership before you start. This takes quarters, it will slow feature work while it runs, and the finish line is not zero monolith. Plenty of successful companies run a smaller calmer monolith surrounded by a handful of services indefinitely, and that is a destination rather than a failure.

the shape of it
TrafficRouterMonolithshrinkingNew serviceone capability1. everything2. most of it3. one path moved4. still working
step 1 of 4
A router in front, one capability carved off at a time, working system throughout.

Worked example

Marco leads platform at a marketplace with a 9-year-old PHP monolith and 80 engineers stuck in a one-deploy-per-day queue. Instead of a rewrite, his team puts Envoy in front and picks search as the first extraction: it has a clean seam, its Elasticsearch cluster already lives outside the monolith, and its traffic peaks 5x higher than checkout's. They ship the search service in six weeks, run it in shadow mode for two more comparing responses (0.3 percent mismatch, all timezone bugs in the old code), then flip the route. Over 18 months, four more services follow: notifications, payments, image processing, and seller analytics. The monolith is still there in year two, one third its old size, deploying in 4 minutes, and nobody is trying to kill the rest of it.

Monolith vs Microservices: wrapping up

In the real world

  • 01Shopify runs a Ruby on Rails modular monolith with millions of lines of code, enforcing internal component boundaries with tooling (Packwerk) instead of network calls, and handles Black Friday traffic on it.
  • 02Netflix migrated from a monolith to hundreds of microservices between roughly 2009 and 2016, driven by team scale and streaming growth, and built much of the now-standard tooling (Eureka, Zuul, Hystrix) along the way.
  • 03Amazon's Prime Video quality-monitoring team published in 2023 that consolidating a microservices pipeline into a monolith cut its infrastructure costs by about 90 percent for that workload.
  • 04Segment publicly documented collapsing over 140 destination services back into a single service in 2018 after shared libraries forced fleet-wide redeploys for every fix.
  • 05Uber moved from its early monolith to thousands of microservices, then introduced DOMA (domain-oriented microservice architecture) to group them into domains because unbounded service growth had made the system hard to reason about.

Questions people ask

How many engineers do you need before microservices make sense?

There is no magic number, but the common experience is that the coordination pain of a shared codebase starts beating the operational pain of distribution somewhere past 50 engineers. Below that, a modular monolith with enforced boundaries usually ships faster. A specific forcing function, like one component with 10x different scaling needs or separate compliance requirements, can justify an earlier split for that component alone.

Can microservices share a database if they own different tables?

Sharing a database server is a pragmatic cost choice some teams make; sharing tables is where independence dies. If two services read and write the same tables, a schema change requires coordinating both, and you have a distributed monolith. The workable compromise is separate schemas or databases per service on shared infrastructure, with all cross-service access going through APIs or events.

What happens to transactions when data is split across services?

You lose ACID across the boundary and adopt the saga pattern: a sequence of local transactions where each step has a compensating action that undoes it if a later step fails. An order saga might reserve inventory, charge the card, then confirm; if the charge fails, a compensating step releases the reservation. It works, but it is real complexity, which is a strong argument for keeping data that must change together inside one service.

Quick review

Monolith:
single codebase, single deploy artifact. Simple to develop, test, debug at small scale
Monolith pain:
as teams grow, deploys are risky (one bug blocks all), scaling is all-or-nothing, tech debt accumulates
Microservices:
each service owns its data, logic, and deploy lifecycle. Teams can ship independently
Microservices added complexity:
network latency, distributed transactions, service discovery, observability, versioning
Distributed transaction problem:
no ACID across services. Use Saga pattern (sequence of local transactions with compensating rollbacks)
Service mesh (Istio, Linkerd):
handles mTLS, observability, and traffic management between services transparently
Rule:
start monolith. Split only when you have a team ownership problem or a specific scaling bottleneck you can measure
the trade-off

Premature microservices is one of the most common architecture mistakes. Don't distribute complexity before you have to.

in the room

Monolith for early-stage products. Microservices when you have 50+ engineers or a clear service boundary with different scaling needs.