API versioning and evolution — the discipline of changing an interface thousands of strangers depend on, the sequel to API Design. Why APIs rot (breaking defined operationally — any plausible client stops working; Hyrum's Law and the contract defined by client code; tolerant vs strict readers and the declared additive-is-safe convention; rot as nameable forces), versioning schemes (path, header, and per-resource schemes relocate the number while granularity, default policy, and lifetime decide the outcome; account-pinned dated versions as the convergent answer for continuous evolution, with the provider carrying the burden), compatibility layers (the transform chain: a latest-only core wrapped in small pure per-version gates — request-forward, response-backward — fixture-tested per version, with the hard species named and versions retired by deleting gates), specs and SDKs (one machine-readable source of truth with bidirectional conformance tests and spec-diff breaking-change detection; generated SDKs owning the mechanical 80% with tolerant-reader behaviors built in; fenced hand-written islands), deprecation (deprecated-but-functional with machine-readable marking, minimum support windows, brownouts, account-level telemetry burndowns, migration tooling absorbing the asymmetry, and slip policies protecting sunset credibility), and designing for evolution (objects and envelopes over bare scalars, open sets with catch-all semantics, expansion over proliferation, the corrected robustness principle — precise acceptance, tolerant reading — evolution reviews, and honest beta ladders). Grounded in Wikipedia (API, backward compatibility, software versioning, HTTP, adapter pattern, middleware, OpenAPI, IDLs, deprecation, product lifecycle, robustness principle, extensibility).
The day an API gets its first external integration, its shape stops being yours. Every field name, every status code, every undocumented quirk becomes load-bearing for code you can't see, written by teams you can't reach, deployed on schedules you don't control. API evolution is the discipline of changing a thing that thousands of strangers depend on — and it begins with understanding exactly what you promised, because most breakage comes from changes the provider genuinely believed were safe.
The real contract is operational: a change is breaking if any plausible client stops working — extending past docs into everything observable (Hyrum's Law), split across tolerant and strict readers. Rot decomposes into nameable forces: design debt, version sprawl, spec drift, zombie clients, change aversion. Opens the series.
Where does the version live — in the path, a header, a date? The debate consumes design reviews and misses the real decision underneath: what a version even is (the whole API? a resource? a behavior?), who chooses it (the request? the account?), and how many will you honestly support? This post surveys the schemes with their true trade-offs, including the account-pinned date scheme that the most evolution-serious API companies converged on — and why.
Path, header, and per-resource schemes mostly relocate the number; the real axes are granularity, default policy, and lifetime. The convergent answer for continuously-evolving APIs: dated versions pinned per account — every breaking change ships independently, integrations keep their era's semantics, the provider pays in transforms.
Promising account-pinned dated versions is easy; the hard part is the server that must honor all of them. The naive answers — fork the codebase per version, or thread `if version < X` through every handler — both collapse within a year. The architecture that scales is a transform pipeline: one current implementation at the core, and a chain of small, testable version-gates that translate requests forward and responses backward. This post builds it.
The transform chain: a latest-only core wrapped in per-version gates — small, pure, fixture-tested pairs of request-forward and response-backward transforms composed in date order. Know the hard species (semantic conversion, information asymmetry, behavioral wraps, fan-in/out) and retire versions by deleting gates.
An API that exists as code, a spec document, hand-written SDKs in six languages, and documentation pages is an API that exists in four slowly diverging copies. The evolution-era answer is to pick one source of truth — the machine-readable spec — and generate or verify everything else from it, so a change made once propagates everywhere or fails loudly. This post is the spec-first pipeline: what it takes, what it buys, and where generated SDKs still need human hands.
One machine-readable source of truth: complete specs, bidirectional conformance tests, dated snapshots per version, and spec-diff breaking-change detection as the evolution linter. Generate the mechanical 80% of every SDK with tolerant-reader behaviors built in; humans keep idiom and the fenced hand-written islands.
Shipping a version is a product launch; retiring one is a diplomatic mission. The clients still on it are, by definition, the ones who didn't upgrade when asked — working code owned by teams with no budget for your roadmap — and behind every deprecation that went badly is an integrator who found out from an outage. Deprecation done well is a designed process: telemetry that knows who's affected, communication that reaches them, migration paths that respect their economics, and sunsets that mean what they say.
Deprecation as designed lifecycle: deprecated-but-fully-functional with machine-readable marking, declared minimum support windows, warning cadences escalating through pre-announced brownouts, account-level telemetry driving targeted comms and burndown curves — and an explicit slip policy protecting sunset credibility.
Every mechanism in this series — versions, gates, specs, deprecations — is the cost of changing decisions already shipped. The cheapest breaking change remains the one you never need, and whether you need many or few is substantially determined at design time, by choices that cost nothing when made and compound forever after. This closing post is that discipline: the additive-evolution patterns, the robustness-principle corrections, and the design review that asks about every surface, "how will this change?"
The cheapest breaking change is the one never needed: objects and envelopes over bare scalars, open sets declared with catch-all semantics, expansion parameters over endpoint forks, names reviewed against their futures — plus the corrected robustness principle and an honest beta ladder. Closes the series.
This series is part of a larger body of work by Pratik Dhanave, an Agentic AI Architect writing about production AI systems, distributed systems, and cloud-native engineering. Explore all course series, browse every post, or find topics via the tag index.