Replace a capability at a time
The Strangler Fig pattern introduces a boundary that routes selected behavior to a replacement while the existing system continues serving the rest. It reduces the scope of an individual cutover, but does not guarantee uninterrupted service or effortless rollback. Pattern guidance.
The first unit should have a clear owner, observable behavior and manageable dependencies. Authentication is often central to many workflows, so it should not be described as universally low-risk or self-contained.
Preserve the request contract
A proxy must handle more than a JSON body. Check methods, query strings, headers, cookies, redirects, status codes, streaming and cancellation. Record how the framework exposes a path after mounting middleware; comparing the wrong path representation can route every request to the wrong backend.
For a web application, rewrites and redirects have different effects. A rewrite maps a request internally while preserving the visible URL; a redirect instructs the client to request another URL. Next.js documents external rewrites and their ordering. Next.js rewrite reference.
Use explicit route rules with tests for boundaries and unsupported methods. Do not proxy requests to an arbitrary client-supplied destination.
Choose one authoritative writer
The tempting migration shortcut is to write to two databases with Promise.all. That does not create a transaction across them. One write can succeed while the other fails, leaving the systems inconsistent.
For an illustrative customer-profile migration, keep the legacy store authoritative initially. Capture changes through an agreed mechanism, backfill historical records and reconcile the target. Track a checkpoint so new writes arriving during backfill are not lost.
Before switching ownership, validate identifiers, relationships, deleted records and important field values. Matching row counts alone cannot establish equivalence.
Define the rollback boundary
A traffic switch can be reversed quickly only if the old system can still understand the resulting data and sessions. Once the replacement writes a new format or performs new external effects, routing back may require a forward repair or data translation.
Write down the cutover conditions:
- Which system accepts writes during each phase.
- How lag and failed synchronization are detected.
- Which version of the schema both systems understand.
- Who can stop the rollout and what that action actually does.
- How in-flight requests and background jobs are drained.
These are design decisions for the application, not universal migration settings.
Compare behavior safely
Shadow reads can reveal differences using sampled, appropriately authorized traffic. Avoid shadowing writes that trigger duplicate external effects. Compare normalized business results rather than requiring byte-identical responses where harmless formatting differs.
Release to a small cohort only when identity and routing remain stable for that cohort. Watch both technical errors and domain outcomes, such as incomplete orders or missing records.
Retire the old path deliberately
Remove the old system after its traffic, jobs, integrations and data obligations have been accounted for. The observation period should cover the actual business cycle and recovery requirements; there is no universally sufficient one- or two-week delay.
The migration succeeds when ownership and operation are simpler and the replacement is demonstrably correct, not merely when the proxy sends most requests to new code.

Sardar Mukhshaf
Full-Stack & DevOps Engineer
I build scalable web applications, secure backend systems, and production-ready cloud infrastructure. My engineering focus spans interface design, API architecture, deployment pipelines, and observability. Follow for more deep dives into system architecture, performance engineering, and emerging technologies.
More from the practice
Keep the useful ideas close.
Follow new articles with your preferred RSS reader.
Open the RSS feed ↗


