API Evolution Lab

Your project API is live and clients depend on it. Six product requirements arrive, one at a time: multiple members, roles, invitations, external guests, thousands of members, async deletion. Evolve the contract at every step without breaking the clients that shipped yesterday.

The contract as it stands
GET /projects/{id}
200 OK
{
  "id": "prj_81",
  "name": "Atlas Redesign",
  "owner": { "id": "usr_1", "name": "Dana" },
  "created_at": "2025-01-12T09:30:00Z"
}
New requirement

Product wants collaboration: a project should have multiple members, not a single owner. The mobile app, a CLI, and two paying customers' integrations all consume this endpoint today.

The pressure

The obvious "correct" model replaces `owner` with a `members` array. But every existing client reads `response.owner.name` — the mobile app renders it on the project card, the CLI prints it, one customer's integration routes approval emails to `owner.id`. Remove or rename the field and all of them break on the same day, with no code change on their side ([[backward-compatibility]]).

How do you evolve the contract? Old clients keep running.