All posts

Migrating a legacy system incrementally: data authority, cutover, and rollback

· 9 min read

Routing users to a replacement does not finish a data migration. Follow an order-system modernization through behavioral baselines, transfer of write ownership, business-level reconciliation, recovery after new writes, and retirement of the legacy system.

Start with a business boundary you can switch

Rebuilding every order-system feature before a final cutover weekend can hide undocumented behavior until late. A strangler fig approach routes between legacy and replacement components and moves functionality incrementally. AWS guidance describes using a routing layer to make that transition gradual.

Choose migration units using business and data dependencies, not screen URLs alone. Splitting one order's create, amend, and cancel paths between systems can lose updates. Route consistently by tenant or order ID, and account for reports and batches spanning those units.

Record current behavior and agree what will change

Screens and APIs miss nightly batches, CSV integrations, and manual corrections. Trace one order through input, reservation, shipment, cancellation, and billing. Inventory all readers and writers. Where behavior is undocumented, record representative outputs and database updates as tests.

Tabulate authority and write ownership at each stage

StageAuthoritative writes for the selected dataReads and validation
PreparationLegacy alone writesContinue legacy operations; transfer initial data and changes
Shadow comparisonLegacy alone writesReturn legacy results; compare replacement reads separately
Pilot readsLegacy alone writesRoute selected reads to replacement; measure lag and impact
Transfer ownershipPause selected writes and drain in-flight legacy updatesSynchronize and reconcile to a defined position; fence legacy writes
Replacement operationReplacement alone writes the selected dataRoute reads there too; define any remaining legacy replication narrowly

Consider change data capture (CDC) or an outbox for transfer. Row replication alone does not establish equivalent business meaning. Define state mapping, deletes, versions, duplicates, and restart positions. Sending the same API request to both systems leaves a partial-success failure path.

Stopping legacy writes includes batches, admin tools, delayed jobs, and webhooks. A routing flag does not stop work already running. Enforce ownership with permissions or ownership-state checks, and explicitly queue requests or return retryable failures during cutover.

Compare the same scope at a comparable point

Shadow validation compares replacement results without returning them to users. Use a path that cannot send notifications, charge accounts, or update stock. Even reads add load, so limit sampling and frequency.

Two systems each holding 100,000 rows can still disagree: a missing order and an unexpected order cancel in the count. AWS DMS validation compares corresponding rows, reports mismatches, and consumes additional resources. A changed schema also needs business-mapping validation above the row comparison.

CompareConditionsDifference handling
Order IDs, states, amounts, currenciesSame scope and mapping rulesSeparate missing, extra, and changed values
Versions and deletion statesAlign replication positions or comparison snapshotsSeparate in-flight lag, true mismatches, and unverifiable records
Shipment, cancellation, and notification outcomesSame business ID and processing stageQuery external results before deciding to replay

Reading actively changing data at different times can turn normal replication lag into apparent errors. Define a comparison position and track unvalidated records separately from real mismatches. A lag metric displaying zero does not by itself prove every record has been applied.

Rehearse the full pause-to-resume sequence

Write-ownership transfer when a brief pause is acceptable; rehearse failures and restart at each stage

Pause new selected writes and drain in-flight legacy updates → Record the final position and apply changes through it → Reconcile business IDs and states; resolve unknowns and mismatches → Fence legacy updates; switch routes and ownership to replacement → Resume replacement writes; verify business outcomes and backlog

Rehearse realistic data volumes and skew, lock waits, replication backlog, interruption, and restart. Measure duration and change the approach if it exceeds the allowed pause. Check the routing layer's capacity, availability, and configuration propagation too.

Expand-contract can stage incompatible schema changes: add a compatible form, migrate consumers, then remove the old form. Keeping columns available does not guarantee value or semantic compatibility. Validate transformations and the versions that run concurrently.

After replacement writes, reversing the route is insufficient

Rollback pointRequired conditionRecovery approach
Replacement has only served readsLegacy remains authoritative and currentReturn reads to legacy; verify caches and routing state
Replacement has accepted writesLegacy can represent and receive every update and deletePause writes, reverse-transform and reconcile, then transfer ownership back
New states or external effects cannot be represented in legacyRouting reversal alone cannot meet safe recovery conditionsContain impact; recover through replacement fixes or business compensation

A replacement-only order state can be lost or misinterpreted by legacy. One option is restricting writes to compatible states during the rollback window. If replicating backward, transfer ownership without allowing both systems to become authoritative simultaneously. Database restoration cannot undo a charge or delivered notification; verify external outcomes and handle business compensation separately.

Include legacy retirement in the definition of done

Incremental migration needs temporary coexistence mechanisms. Budget their maintenance and define removal conditions during design. Progress to the next unit when business outcomes and ownership transfer are verified, alongside the replacement interface working.

Primary sources for migration patterns and validation

Tips this post digs into