Migrate a permit case API while release 47 remains cached in browsers, release 63 rolls out by region, and district 29 runs a monthly export. The old representation reports durationHours and three case statuses. The new representation uses durationMinutes and introduces awaiting-evidence. Build one domain case loader with current permission checks, then wrap it in two explicit representation adapters. A new server must coexist with an old client before any field is removed. The old client must show a safe read-only state for an unfamiliar status. The project ends only after a full consumer cycle and a reversible old-route shutdown drill.
Project: permit API client migration
Build contract
- Define both response shapes, units, null rules, error behavior, version selection, and version-aware cache identity.
- Run release-47 and release-63 clients against old, expanded, and contracted server fixtures; include mixed-region rollout and rollback.
- Track daily and monthly consumer use, announce old-route retirement, and rehearse a reversible shutdown without bypassing permission checks.
Implementation checkpoint
function durationForClient(minutes, clientVersion) {
if (clientVersion === "v1" && minutes % 60 === 0) return { durationHours: minutes / 60 };
if (clientVersion === "v2") return { durationMinutes: minutes };
throw new Error("Unsupported duration representation");
}
console.log(JSON.stringify(durationForClient(180, "v2")));
// Output: {"durationMinutes":180}Cost and boundaries
The two adapters add O(N) serialization work for an N-record page, while a mixed-version cache lowers hit rate. Keep domain authorization shared so the old API cannot become a permission bypass. A full client-server test matrix has O(C × S) combinations for C supported clients and S active server phases; a stated support window keeps that number bounded. The monthly job sets the observation period for retirement. Measure actual calls by client identity, old-route error rate, status-decoder failures, response bytes, and rollback time. Store no tokens or private record IDs in the migration dashboard.
Failure drill
Deploy the new client to region A while region B still returns only the old shape. The new client must stay readable or wait behind a rollout gate. Change an old field from hours to minutes without renaming it and verify a contract test blocks the release. Send awaiting-evidence to release 47; its Close action must be unavailable. Mix version-selected responses behind one cache key and catch the wrong representation. Retire v1 after a short quiet period, then run district 29’s monthly export; the drill should reveal why a full cycle and a registered owner matter. Roll back the route toggle and confirm both versions still enforce current case permission.
Acceptance checks
- Both supported clients show safe state and correct duration units in every active server phase.
- Version-selected responses cannot cross a shared cache or pagination cursor boundary.
- A monthly consumer receives migration notice and passes its replacement export test.
- A revoked reviewer is denied through every version before and after rollback.
Common Mistakes
- Testing only the latest client and server together.
- Changing field meaning without a new representation.
- Equating deprecation notice with a safe shutdown.
Related lessons
API Contract Evolution and Client Migration; Response Shape Evolution and Unknown Values; API Version Selection and Representation Scope; Deprecation, Sunset, and Consumer Evidence; Consumer Contract Matrix and Expand-Contract Release.
