Specs and SDKs — Making the Contract Machine-Readable and the Clients Generated

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.

Post 3 made many versions servable; this post makes them describable — because every version you serve is a contract someone needs in their language, and hand-maintaining N languages × M versions is how spec-reality drift (post 1) becomes a support department.

Spec as source of truth, enforced

Declaring OpenAPI (or your IDL of choice) the source of truth is a sentence; enforcing it is a pipeline. The components that make it real:

Generated SDKs: the leverage and the limits

With a trustworthy spec, SDK generation turns N-language maintenance from authorship into review. The honest accounting: what generation does well — types and models (strict-reader clients, post 1, with the tolerant-reader escape hatches generated in: unknown-field retention, open enum handling), request builders, auth plumbing, pagination iterators, retry-and-idempotency scaffolding — the 80% that is mechanical and error-prone by hand. What still needs hands — idiomatic surface (a generated Go client that feels like Java is a generated client nobody enjoys; per-language templates carry the idiom), convenience layers (the three-call workflow every integrator performs deserves a helper no spec describes), and the handful of endpoints whose semantics resist schema description (file streams, webhooks setup — hand-written islands in generated seas, clearly fenced so regeneration doesn’t flatten them). And what generation changes about process — SDK releases become pipeline runs keyed to spec versions: a dated API version ships with its SDKs the same day, SDK changelogs derive from spec diffs, and the version a client library targets is explicit in its own metadata (completing post 2’s observability loop from the client side). The same spec feeds the rest of the surface — reference docs, interactive explorers, mock servers for integrator testing (Testing at Scale’s sandbox chapter benefits directly), and the request validators at your own edge, which closes the loop: the artifact that documents the contract is the artifact that enforces it.

The takeaway: evolution at SDK scale requires one machine-readable source of truth — a complete spec, conformance-tested against the implementation both directions, versioned alongside the transform chain, with breaking-change detection running on every diff. From it, generate the mechanical 80% of every SDK (types, builders, pagination, retries — with tolerant-reader behaviors built in), keep humans on idiom and convenience, fence the hand-written islands, and ship SDKs as pipeline runs synchronized with dated versions. One change, one place, everywhere at once — or a loud failure telling you why not.

Key takeaways

Further reading

Sources & References