Case study - monolith to microservices

Three people finished a two-year-stalled lending migration in two months

Smava, one of Germany's largest consumer-loan marketplaces, tried twice to move a single loan-review workflow out of a monolith shared by ~10 teams into its own microservice — and failed twice. Here's the order of operations that finally worked.

Client: SmavaConsumer-loan marketplace · BerlinMonolith → microserviceAPI compatibility · shadow traffic
2 yrs
2 months
stalled, then shipped
3
people on the team
0
rollbacks · 0 frontend rewrites
±0%
latency change

What had already been tried

Two teams. Two years. Two dead ends.

The first attempt cleaned up the domain before the team understood all the production rules. The second got further, but cutover still exposed bugs users could see.

Attempt one

Refactored the domain first

>10x slower

Cleaned up the data model before touching infrastructure. The new service was correct — and an order of magnitude slower than the system it replaced.

Attempt two

Rewrote it, then tried to cut over

Rollbacks

Bugs only surfaced in production. Every cutover attempt was reverted, and the fix on the table was a full frontend rewrite.


The diagnosis

Both teams tried to improve the system before they understood it.

Ten teams had spent years adding one-off business rules to this workflow — rate exceptions, regional overrides, edge cases nobody wrote down. Rewriting the domain model first meant rewriting rules nobody could fully enumerate.

The insight that unstuck it: don't try to know the rules in advance. Build a new service that mimics the old one exactly, then let production traffic itself tell you where the two disagree.


The fix

Match the old API exactly. Clean up later.

No frontend changes, ever. The new microservice had to be a silent, drop-in double for the workflow still living inside the monolith — then the internals could get prettier in peace.

01

Copy the request

Every real request still goes to the monolith. A copy is mirrored to the new service.

02

Run it for real

The new service processes the mirrored copy end-to-end — same inputs, same rules it knows about.

03

Diff the two answers

Old response vs. new response, compared automatically. The customer only ever sees the old one.

04

Fix the gap

Every difference is a missing business rule. Patch the new service, not the old one.

05

Repeat, then cut over

Reads first, queue by queue, then writes — until a queue runs with zero diffs, then cut over for real.


The core mechanism

Shadow-testing turned the migration into a comparison loop.

The monolith keeps serving every live request — and quietly checks the new service against itself in the background.

How the shadow test worked
The monolith keeps serving live traffic — and verifies the new service in the background
Live path — synchronous, client waits Background check — synchronous calls Through the queue — asynchronous, non-blocking
SYNCHRONOUS · CLIENT WAITS FOR THE RESPONSEREST requestREST responseClientuser · UIenqueues result{ request + monolith's response }reads from queuesame requestresponsemonolith compareslogs cleanMonolithserves the client,compares & logsQueueFIFO · call resultsNew microservicereplacement candidateError logonly on mismatch (else nothing)Cutoverno rollback ✓1234567engineers fix the service from the logs → repeat steps 1–7
The synchronous “client → monolith → client” loop stays unchanged. The monolith runs the comparison against the new service in the background — decoupled by a queue so the client is never slowed — and writes an error log only where the responses diverge. Once the mismatches stop, the team cuts over without a rollback.

The outcome

Two months. Three people. Zero drama.

2 mo
total time, after ~2 stalled years
3
engineers, one small team
0 / 0
rollbacks / frontend rewrites
0
new incidents
±0%
change in latency
1
team now owns it outright

“The important part was that our users never became the test plan. We proved the new service against the old behaviour in production before the final switch — so cutover was a formality, not a gamble.”

- Engineering lead, Smava


If this sounds familiar

You may not need a bigger migration team.

You may need a smaller proof: API compatibility first, shadow traffic second, cutover only when the differences stop.

  1. 01A migration off the monolith has already failed once or twice.
  2. 02The next plan keeps expanding into UI, data, and domain redesign.
  3. 03Tests pass, but production keeps revealing missing edge cases.
  4. 04Cutover depends on users finding fewer bugs than last time.
  5. 05The monolith changes faster than the migration team can isolate it.

Stuck on a legacy migration of your own?

We take on the migrations other teams have already tried and stalled on. Shadow-testing works for more than lending — API-compatible cutover paths, comparison loops, and the engineering to replace risky monolith workflows safely.

Plan a migration audit