Chapter 23 of 27 · Part 10: Refactoring to microservices
Refactoring: the strangler application
Migrate a monolith step by step: set a goal, strangle it with new services, and glue old and new together.
key idea / coordinatorservice / good pathin progress / watch outproblem / failureneutral
Should you refactor?
Migrate only when the monolith causes real business pain.
Should you move, and where to start
Chapter 2 begins by challenging the move itself. Istio went back to a monolithic control plane and Segment moved back to a monolith after a small team drowned in per-service deployments. Ask what you want to solve, not whether the architecture is modern. From Practical Event-Driven Microservices Architecture, ch. 2.
Common claim
Why it may not justify the move
"We can scale horizontally"
A monolith can run many copies. The real pain is usually the database.
"Teams will be autonomous"
A modular monolith can give autonomy too. It is partly a mindset change.
"Features will ship faster"
For a small team it can slow things down. Find the root cause of slow delivery first.
"Continuous delivery"
Possible with a monolith, though harder as the team and code grow.
Strong reasons: the database can only scale vertically and has become too costly; the team has outgrown the application; the stack is deprecated or you need several technologies.
Measure progress against the reason you chose, and ask the engineers how it feels. Their feedback is the most valuable metric the book mentions.
If your reason is
Watch
Team autonomy
Dependencies between teams, feature branch lifetime, merge requests
Continuous delivery
Deployments per period, cycle time, lead time
Scalability
Incidents during load peaks, response time, machine tier needed
Set a goal first
Building Microservices opens its migration advice with a warning: microservices are not the goal. Name the outcome you want, such as handling more load or shipping faster, and check whether something easier would deliver it. Running a few more copies of the monolith behind a load balancer may beat a long decomposition.
Then move in small steps. A big-bang rewrite guarantees mostly a big bang. Chip away, put one or two services into production and see whether you got closer to the goal. The monolith is rarely the enemy and often stays, smaller. If scaling is the problem, removing the 10 percent that is bottlenecked may be enough.
Take care not to split too early in an unfamiliar domain. The Snap CI team guessed wrong boundaries, paid for constant cross-service changes and went back to a monolith. Having an existing codebase to learn from makes decomposition easier than a microservice-first start. Newman also warns that you will not feel the real pain of microservices until you run them in production.
The strangler application
Grow new services around the monolith until it fades away.
Code, data or UI first?
Once you pick a service, you still have to order the work inside it. Building Microservices and the Decomposing the Database chapter weigh three choices.
Example: extracting a wishlist from the monolith.
Order
Good
Watch out
Code first
Fast visible benefit. Shows which data the service needs.
Teams stop early and keep a shared database.
Data first
Finds broken joins, integrity and transaction problems early.
Little short-term benefit. Needs a monolith you can change.
Both at once
One step.
A big step with late feedback. Avoid it.
Newman's own take: if you can change the monolith and worry about performance or data consistency, split the schema first. Otherwise split the code and let that show who owns the data.
The UI is easy to forget. Split it too, or the benefit stays siloed.
The user interface often lags behind the back end, because you need the services before you can see how to split the UI. Do not let it lag too far. For a website, page-based decomposition is a simple default: no fancy browser code, a link is a hand-off. Widget-based decomposition is the micro-frontend style, where a team owns a widget and its supporting service.
Safe switching tools
The strangler fig is the core idea: put a proxy in front of the monolith and send each call to the old or the new code. Building Microservices adds tools that make the switch safe. The monolith often needs no change, since it does not know it is being wrapped.
Feature toggle: a switch between the monolith and the new service. It can live in the proxy. A plain configuration file is a fine start.
Canary release: only a small group of users hits the new code first, so a bug hurts few. Tools can ramp up the share when error rates stay low.
Parallel run: send the same request to both and compare. Pick one as the source of truth, normally the old one, and use the other only to check.
Deploy is not release: install the new version first, then release it to users. Blue-green deployment is the simplest example.
New features and vertical slices
Stop growing the monolith, then cut capabilities out of it one slice at a time.
Integration glue
A service and the monolith must read each other's data and react to each other's changes.
Moving a module out with events
Stateless code is easy to move. Data is the hard part. The book walks through moving a reporting module in small steps, never as a big bang. From Practical Event-Driven Microservices Architecture, ch. 2.
A durable broker makes this cheap. If the new service has a bug you reset its offset and regenerate. If reports need new fields you read from the beginning. Once order management is itself extracted, the reporting service reads the new service stream without any change.
This differs from publishing events as a side effect: the message becomes the trigger for the process. If the monolith cannot read a queue, convert requests to messages outside it, or split the monolith flow at the first step.
Dependencies and living with two sources of truth
A new service often still needs data the monolith owns, and the monolith may still need data you moved. A remote call is the quick answer. Keep it temporary: if the monolith slows or crashes, the new service fails with it, and scaling the service adds load to the monolith. From Practical Event-Driven Microservices Architecture, ch. 2.
Going the other way, the monolith often joins data from many domains in one query. Joining over HTTP means pulling and merging a lot of data in memory. The sustainable choice is to feed the monolith database from the new service events, so it becomes a denormalized view while the service is the source of truth.
Sometimes two entry points must live side by side for years. In the book the inventory logic sat in stored procedures that joined order and product data and could not be switched off. Both sides then publish and consume each other changes, and the cycle is broken with an origin header.
Last resort: the book calls two-way sync extremely hard to run for long. Incidents are harder to trace and new features are limited by the monolith design. Keep it near the bottom of the toolbox, and aim to retire the monolith queue when no messages flow.
Consistency and security
Extraction turns ACID transactions into sagas. Plan the order so the monolith never has to undo.
Two worked examples
A brand-new feature built as a service, and an existing capability pulled out.
Recap
Refactor only for real pain, and never with a big bang rewrite.
Use the strangler pattern: new features as services, then extract capabilities.
Integration glue: query APIs or replicas, messages, and an anti-corruption layer.
Order extractions so the monolith stays the pivot and never compensates.
Let the monolith's login set a JWT cookie so new services can authorize users.
Name the goal before you start. Try easier fixes, chip away in small steps and pick easy-but-useful services first.
Challenge the move: pick one measurable reason, start with an easy, valuable, delay-tolerant module and shift data with events and traffic with a proxy.
A remote call to the monolith is a temporary bridge. Prefer an internal view fed by events. Avoid two sources of truth; if you must, use an origin header.
Quick check
Why extract Order Service before Kitchen Service?Show answer
Then the monolith's step is the saga's pivot and never needs undoing, so all the compensation logic lives in the new service instead of being added to the monolith.
Your first idea for the migration is to move order management, which touches four other modules. Why might reporting be better?Show answer
It has fewer dependencies, delivers value, and tolerates stale data, so you meet distributed-system problems at a small scale and get feedback early.