# API Contract Evolution

> Evolve REST APIs safely for clients you do not control — mobile apps and third-party integrations. Use when versioning an API, deprecating endpoints or fields, classifying breaking changes, or setting up backward-compatibility testing.

- Skill: `rockclaver/api-contract-evolution` (Agent Skill)
- Install (CLI): `npx skillmds@latest add rockclaver/api-contract-evolution`
- Raw SKILL.md: https://api.skillmd.com/api/skills/rockclaver/api-contract-evolution/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: rockclaver (https://skillmd.com/u/rockclaver)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/rockclaver/api-contract-evolution

---

# API Contract Evolution

Change APIs without breaking clients you can't force-upgrade. `design-api` covers writing new endpoints; this covers changing them over time.

## Versioning Strategy — Choose One

| Strategy | Use when |
|---|---|
| URL path (`/v1/`, `/v2/`) | Public APIs, mobile clients — visible, cacheable, easy to test. Preferred. |
| Header (`API-Version: 2024-01-01`) | Internal service-to-service where all callers are under your control. |
| Query param (`?version=2`) | Avoid — breaks caching and is easy to forget. |
| GraphQL field evolution | Add fields, `@deprecated`, remove only after confirmed zero usage. |

## Breaking vs Non-Breaking

**Non-breaking (safe without a version bump)**
- Add optional request fields with sensible defaults.
- Add response fields (test that clients actually ignore unknown fields).
- Relax validation; add new endpoints or enum values.

**Breaking (require a new version or deprecation window)**
- Remove, rename, or change a field's type or semantics.
- Tighten validation; change response structure or HTTP method semantics.
- Remove an endpoint.

## Deprecation Lifecycle

1. Add `Deprecation: true` and `Sunset: <RFC 7231 date>` response headers.
2. Log usage per client (API key, user agent, IP).
3. Notify consumers with usage evidence before sunset.
4. Return `410 Gone` after sunset — keep the route registered so clients get a clear error.
5. Remove code after monitored traffic reaches zero.

Minimum window: **3 months** for external APIs; **6 months** for mobile (app store cycles delay adoption).

## OpenAPI as Contract Source

- One OpenAPI spec per version; generate docs, SDKs, and mocks from it.
- CI: `oasdiff` or `openapi-diff` to detect breaking changes. Fail the build on unversioned breaks.
- Consumer-driven contract tests (Pact): consumer publishes a pact; provider CI verifies on every change.

## SDK Generation

Generate typed SDKs with `openapi-generator`, `fern`, or `speakeasy`. Clients pin the SDK, not raw HTTP. Never auto-publish a major SDK version without a migration guide.

## Backward-Compatibility Testing

Record golden request/response pairs from production. Replay against the new version in CI; assert responses match the schema the old client expects. Tools: VCR (Ruby), `pytest-recording` (Python), Polly.js, MSW record mode.

## Guardrails

- Never change a field's meaning without changing its name — semantic drift is invisible to type-checkers.
- Never deprecate without tracking real usage first — unknown callers break silently.
- Never set a sunset date shorter than your longest release cycle.
- Never rely on clients ignoring unknown fields without a test confirming they actually do.

