# Keep old clients working through an API rollout

40-minute engineering lab · Node.js 24 · Standard library · Original synthetic case

An API deployment does not update every consumer. An operator may keep an old tab open while a new server starts returning a new result. In this lab, a correctly withheld draft becomes a misleading **Complete** message in the old client. You will reproduce that failure over real local HTTP, preserve a useful fallback, and inspect both rollback directions.

Download the [contract-rollout companion](/templates/fde-contract-rollout.mjs). It starts a temporary server on `127.0.0.1`, makes requests to that server and closes it. No packages, accounts, model calls or external services are needed. The clients are JavaScript consumer functions with synthetic state, not browser applications. There is no real authentication, sending, data migration or deployment. Server outcomes are authored fixtures: the timeout case returns a manual-timeout body immediately; it does not induce a network timeout. What you execute is the HTTP exchange and the consumer’s resulting state.

## 1. Learn the three contracts

A **producer** supplies the response; a **consumer** interprets it and decides what the person can do next. **Wire compatibility** concerns communicating and decoding the representation. **Semantic compatibility** also concerns its meaning and behavior. A response can parse correctly while giving the operator a dangerous impression.

The fictional service helps draft administrative form-location replies. U1 and U2 are consumer versions; A1 and A2 are API implementations. The selected request and unfinished operator note belong to the client state. Neither an API response nor a client upgrade should silently erase that work.

| Implementation | Contract in this exercise |
| --- | --- |
| A1 | Returns a known draft or manual outcome. Ignores the new contract-selection header. |
| U1 | Understands a draft and manual reasons `timeout` / `missing-source`. Its deliberately flawed final HTTP 200 branch says Complete and provides no manual action. Non-2xx responses use its existing unavailable/manual path. |
| U2 | Accepts documented version-1 and version-2 results. Unknown versions, malformed bodies and unknown results use an unavailable/manual path. |
| Compatible A2 | Supplies version 2 only when explicitly selected. Keeps honest version-1 outcomes for old clients; interrupted coverage without version-2 selection returns HTTP 503. |

These are deliberately narrow, exact-shape consumer checks. An added field in an otherwise known body also reaches U1’s misleading Complete branch; do not infer tolerant parsing from its age. Other clients may allow extra fields, and need their own demonstrated contract.

`X-Training-Contract: 2` is a custom selection field for this exercise. It selects a response representation; it supplies no identity or permission. An unsupported value receives HTTP 400 from compatible A2. This is an explicit fixture policy, not a universal API-versioning standard.

## 2. Follow the response into the operator state

A2 introduces a distinction between an empty lookup and unavailable source coverage:

```json
{"contractVersion":2,"kind":"manual","reason":"source-unavailable"}
```

Returning that object on HTTP 200 to U1 reaches its unknown-result branch. The API withheld the draft, but the client reports Complete. Adding a response enum can therefore break the task even when serialization succeeds.

A compatible server does not rename unavailable coverage to `missing-source` or `timeout` merely to fit the old enum. In this fixture, HTTP 503 takes the old client's already-supported unavailable state and exposes manual lookup. U2 can request version 2 and explain the new reason. A supported draft remains a proposal for operator review, never permission to send.

The visual below is an authored interpretation of the cases. Compare it with the output you execute. HTTP 503 is useful here because U1's fallback is known; a real client that loses work on non-2xx would need another verified transition.

## 3. Predict and run the version pairs

Read the downloaded file, then run it from your own working folder:

```sh
node --version
node fde-contract-rollout.mjs --all > original-results.json
```

Before execution, predict the HTTP status, operator state and manual-action availability for each row. Afterward, add the observed response and whether the request and unfinished note survived. Keep a mismatch with your first prediction.

| Case | Pair and input | Prediction to write first |
| --- | --- | --- |
| `legacy-timeout` | U1 / A1; timeout | Does manual work remain available? |
| `broken-overlap` | U1 / unadapted A2; interrupted coverage | Which consumer branch handles the new HTTP 200 body? |
| `server-rollback` | U2 / A1; timeout | Can the new reader still understand the old producer? |
| `client-rollback` | U1 / compatible A2; interrupted coverage | Does the new producer still support the old reader? |
| `explicit-v2` | U2 / compatible A2; interrupted coverage | Which header and response identify the selected representation? |
| `supported-v2` | U2 / compatible A2; supported draft | Is the next action review or automatic sending? |
| `unsupported-selection` | U2 / compatible A2; unsupported contract | Is the selection rejected while preserving manual work? |

To inspect one exchange:

```sh
node fde-contract-rollout.mjs client-rollback
node fde-contract-rollout.mjs server-rollback
```

Trace selection → actual HTTP status/body → consumer interpretation → retained work. The two rollback cases execute mixed-version pairings. They do not perform a rolling deployment or prove recovery of database changes. Their value is exposing a compatibility assumption before the team relies on it.

Expected interpretation after you have predicted: the deliberately broken overlap reports Complete without manual action. The compatible old-client coverage case uses 503 and keeps manual lookup. The newer client reads A1's known timeout after server rollback and reads A2's explicit version-2 manual reason. Unsupported selection stays unavailable. The supported draft remains reviewable. Compare actual output rather than copying these expectations as run evidence.

## 4. Break one compatibility control

Use the companion's deliberate fault switch and retain the new output separately:

```sh
node fde-contract-rollout.mjs --all --break-compatibility > changed-results.json
```

This removes compatible A2's old-client coverage fallback. Predict which row changes. Compare `client-rollback` with `explicit-v2`: the old client now receives the new HTTP 200 body and reaches Complete; the explicit version-2 consumer should behave as before. An all-new-client demonstration would miss the regression.

Explain the smallest repair: restore the representation boundary for consumers that did not select version 2. Do not weaken U2's validation or mislabel the source failure. Rerun the affected pair against the original behavior, keeping both outputs and the fault-switch setting. You observed a controlled local fault, not a production incident.

## 5. Make the receiving team own the next check

In a three-person pod, assign API owner, consumer owner and receiving engineer. Each person predicts a different pair before discussing it. The receiver runs one rollback pair and the changed-control case without the author navigating. Record hints or takeover. For solo work, label the receiving check self-review.

```text
Candidate file / version or digest:
Pair and fault-switch setting:
My original prediction:
Observed request selection / status / body:
Consumer state / manual action / retained request and note:
My contribution and assistance received:
Receiver action actually performed:
Accept for this stated purpose, or return this specific gap:
Next owner / next check / evidence still missing:
```

The API owner must define the supported overlap and how to stop exposing the new representation. The consumer owner must verify unknown-result behavior and preservation of real unsaved work. The receiving engineer must locate the exact candidate and reproduce both rollback pairings. The client workflow owner decides whether the fallback is usable for the agreed task. A pod lead coordinates these responsibilities; they do not inherit them from a successful command.

Before retiring version 1, identify active integrations, long-lived tabs, queued work and clients outside your release control. Establish an agreed migration path and receiving-owner evidence. Elapsed time and a successful new-client screenshot are insufficient. For this lab, report only local HTTP and consumer-function observations; browser usability, authorization, live-user impact and placement readiness remain unestablished.

## 6. Explain, debug and extend

**Explain:** Why can a new response enum break an otherwise valid API response? Name the producer change and the exact consumer branch.

**Debug:** A team tests only U2/A2 and says rollback is safe. Identify the two missing pairings and the evidence each would supply. Explain why server rollback and client rollback are different checks.

**Design:** The real old client erases unsaved work on HTTP 503. Which assumption in this exercise no longer holds? Propose a transition with a verified usable path before exposing the new result. Do not prescribe 503 as the answer when its consumer behavior is the defect.

Continue in the [deployment and handoff lesson](/app/academy?course=fde-client-delivery&lesson=deployment-and-handoff&view=focus) under existing Academy access. Add the results to the [client-delivery workbook](/training/guides/workbook) alongside the rollout agreement and receiving-team runbook.

## Sources and scope

Original LockedIn Labs case and code. References checked 25 October 2026:

- [Google AIP-180: backwards compatibility](https://google.aip.dev/180) distinguishes compatibility dimensions and cautions that consumers can mishandle new response enum values.
- [AWS Builders' Library: ensuring rollback safety during deployments](https://aws.amazon.com/builders-library/ensuring-rollback-safety-during-deployments/) explains why protocol transitions must remain usable across mixed versions and reversal.
- [RFC 9110: HTTP semantics](https://www.rfc-editor.org/rfc/rfc9110.html) defines HTTP status semantics. The custom header and fallback mapping are this exercise's declared contract.

These are learning references, not vendor affiliation or certification. Completion of this exercise is practice evidence, not independent qualification or approval to operate a client system.
