A misleading equivalence
“We can always go back” can mean two very different things: the old traffic path still exists, or the old system can correctly continue with the state created since migration. The first does not establish the second.
If a new application writes data in an incompatible schema, changing routing is not yet recovery. An old deployment file is also insufficient when its secrets, dependencies or permissions have changed.
Three boundaries I would examine separately
Traffic: which clients already reach the new destination? DNS TTLs, existing connections and load-balancer draining can delay the transition. A successful configuration update does not mean every request has left the old path.
Data: who may write during each phase? A system reachable in parallel should not accidentally become a second independent writer. Replication, write ownership and consistency need to fit the actual service.
Compatibility: which combinations of old and new application, schema and configuration are valid? A forwards- and backwards-compatible transition can preserve options. Whether it is possible needs to be tested.
Illustrative model · not live infrastructure
Read the transition.
Current operation
Production traffic stays on the existing path. Record dependencies, write ownership and the criteria for switching.
Return path: the original system remains authoritative.
Parallel validation
Validate the new destination using representative requests and the intended identity. Parallel reachability does not automatically mean parallel write ownership.
Return path: stop testing; retain the production path.
Traffic cutover
Shift traffic using the planned procedure. Observe connections and data state separately. Whether reversal remains valid depends on the changes that have actually occurred.
Return path: only while data and compatibility conditions still hold.
A rollback condition needs evidence
Before switching, I would name a few decisive signals: a representative business request, errors and latency for that path, and an integrity check for its data. Define an observation window, responsible people and the boundary beyond which a simple reversal is no longer valid.
Checks should use the identities and network paths expected in operation. A successful test with administrator privileges or from another subnet can omit the very restriction that matters after cutover.
What I want to see before proceeding
A current path, a new path and an explicit transition between them. A return path whose prerequisites still hold. And a person who can decide using agreed evidence. The plan need not be elaborate. It needs to expose the assumptions carrying the risk.
Reproduce it: traffic returns, data does not
Reproducible learning example using synthetic data. Not an account of an employer or client project.
The symptom: after traffic reversal, the old application runs again but fails on records that were valid before cutover. This local example isolates that boundary.
Run locally
Python 3 is sufficient. The script uses an in-memory SQLite database, writes no files, and needs no network or cloud access. Download it, then run it in a terminal:
Download the Python example · rollback-compatibility.py
python rollback-compatibility.pyExample output
Before cutover: old reader accepts 'paid'
New writer: status changed to 'refunded'
After traffic reversal: unsupported status: refunded
Simple rollback gate: BLOCKED (data contract is incompatible)The old application accepts only “new” and “paid”. The new one writes “refunded”. Even without a change to the SQL column, the data contract is now incompatible. Switching to old code does not undo that new meaning. The script checks both states with assertions.
What decision follows?
Before release, test compatibility with data the new writer can actually produce. One possible transition: prepare every reader for the new value before enabling the writer. Whether old values can be restored without losing meaning is a separate domain decision. Forcing “refunded” back to “paid” would not be a valid repair here.
This model checks a data contract. It does not test AWS routing, replication lag, concurrent writes, or full recovery. Those boundaries belong in a service-specific cutover test.