Lesson 22 / 27
Compatibility Layers and Gradual Migration
Ease migration with adapters, feature flags and client SDK updates.
Make the new path the easy path
Migrations succeed when moving is cheap. Offer translation layers on the server so old requests are mapped onto the new model, publish updated SDKs and a codemod or script for mechanical renames, let clients opt in per feature or per request (a header, a flag or the date version) instead of flipping everything at once, and keep both versions in the same test suite so you notice drift. For breaking behavioural changes, run old and new side by side in shadow mode (compare responses without affecting users). Keep your own services off deprecated versions first; if the API team does not use v2, nobody will trust it.
A v1 adapter on top of the v2 model (illustrative)
v1 responses are produced from the same record by joining the split fields back into one name, so there is no second data path to maintain.
def to_v2(item):
return {"id": item.id, "first_name": item.first_name,
"last_name": item.last_name, "plan": item.plan}
def to_v1(item): # adapter: same data, old shape
v2 = to_v2(item)
return {"id": v2["id"], "name": f"{v2['first_name']} {v2['last_name']}", "plan": v2["plan"]}Quick check: Why compare old and new responses in shadow mode?
- To delete data
- To double the bill
- To avoid testing
- To find differences before users are affected
Answer
To find differences before users are affected — Shadow traffic reveals behavioural mismatches without risking production users.