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.
GET /projects/{id}
200 OK
{
"id": "prj_81",
"name": "Atlas Redesign",
"owner": { "id": "usr_1", "name": "Dana" },
"created_at": "2025-01-12T09:30:00Z"
}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 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]]).