In an OpenAPI workflow, what is the difference between contract-first and code-first, and what does each cost?
answer
- Which artefact is authoritative
- Derivation runs one way or the other
- Mocks before implementation exists
- Generated specs are always fresh, never reviewed
- Commit the generated file and diff it
basics
~20 sContract-first treats the hand-written OpenAPI document as the source of truth and derives code from it; code-first treats the implementation as the truth and generates the document from annotations. The first buys design review and parallel work, the second buys speed and automatic freshness.
solid answer
~50 sIn **contract-first**, the document is authored and reviewed before implementation, then drives everything downstream — server interfaces, client SDKs, mock servers. Consumers can start against a mock the day the spec merges, the API gets reviewed as a design artefact rather than discovered after release, and every language sees the same contract. The costs are real: someone must be fluent in OpenAPI, generators constrain what you can express, and the implementation can still drift from the document unless something checks. In **code-first**, annotations on the implementation produce the document. It is cheaper to start, and the spec always reflects the code that generated it — but the API is designed implicitly, the published contract can change silently because someone renamed a field, and the document inherits the framework's serialisation quirks. The pragmatic middle is code-first with the generated spec committed and a CI check that fails on an unreviewed diff.
go deeper
Be able to state which artefact is authoritative in each approach and that everything else is derived from it.
Explain the tradeoffs concretely: mock servers and design review versus speed and automatic freshness, and why a generated spec is fresh but unreviewed.
Show the enforcement thinking — generated interfaces that break the build, a committed spec with a CI diff gate, and where drift actually creeps in under each approach.
Own the fleet-level decision: which services must be contract-first, how SDKs are produced and supported, and the migration cost of changing direction across many teams.
## Two directions of derivation The difference is simply which artefact is authoritative. **Contract-first**: a human writes the OpenAPI document. Everything else — server interfaces, client SDKs, mocks, documentation — is derived from it. The document is reviewed like any design. **Code-first**: a human writes the implementation, annotated with metadata; a library walks the running application or its types and emits the document. The document is a build output. Everything else follows from that choice of direction. ## What contract-first buys **Design review before implementation.** The API surface arrives as a reviewable diff before anyone builds it. Naming, resource shape and error modelling get argued when changing them is free, not after a client has integrated. **Parallel work.** A merged spec can drive a mock server immediately, so consumers build against a realistic API while the implementation is still in progress. On a project with a separate frontend or partner team, this alone often justifies the approach. **Cross-language consistency.** Every SDK is generated from one document, so field names, nullability and error shapes are identical everywhere. In code-first, each service's spec reflects its own framework's conventions and the fleet drifts apart. **Compile-time enforcement.** When the server implements a generated interface, a contract change breaks the build at the exact points that no longer satisfy it — the strongest guarantee available that code and contract agree. ## What contract-first costs **Skill and time.** Somebody must know the specification well. Badly hand-written specs — untagged operations, missing operation ids, anonymous inline schemas — produce worse SDKs than a decent code-first setup. **Generator constraints.** You end up designing within what generators handle well across your target languages. Advanced schema composition is supported unevenly, and discovering that after the design review is expensive. **Friction on small changes.** Adding one field means editing the spec, regenerating, and implementing — heavier than typing a field into a class. **Drift is still possible.** Implementing a generated interface prevents it structurally; hand-implementing against a document does not. Without a check, contract-first can be exactly as wrong as code-first, only with more ceremony. ## What code-first buys and costs It is fast to adopt: annotate, and the document appears. The output is always consistent with the code that produced it at that moment, which is genuinely valuable — there is no separate artefact to forget. The costs are subtler. The API is designed by accident: whatever the class hierarchy happens to serialise to becomes the contract, and internal refactors surface as public changes. Nothing forces a review of the API surface itself, so a renamed field ships as a breaking change nobody discussed. Descriptions and examples are annotation clutter, so they tend to be sparse and the rendered documentation is thin. And the document inherits framework quirks — how the serialiser represents dates, empty collections and polymorphic types leaks into the published contract. ## The hybrid most teams land on Generate the spec from code, **commit the generated document**, and have CI regenerate it and fail if the committed file differs. That gives three properties at once: the spec cannot drift (the check enforces it), every contract change becomes a visible diff in code review (so nobody renames a public field by accident), and you keep code-first's low ceremony. Layer a linter on top for house style and a breaking-change diff against the last published version, and you have most of contract-first's discipline without hand-authoring. ## Choosing Contract-first pays off when multiple teams or external partners consume the API, when several languages need SDKs, or when the API is the product. Code-first fits an internal service with one consumer moving fast. The choice is not permanent — but migrating a fleet is expensive, so it is worth making deliberately rather than defaulting to whatever the framework does out of the box.
- Does contract-first guarantee the implementation matches the document?Only if something enforces it. Generating server interfaces and implementing them does — a contract change breaks the build at the mismatch. Hand-implementing against a document does not; then contract-first drifts exactly like code-first, just with more ceremony. The guarantee comes from the enforcement mechanism, not the authoring order.
- What is the cheapest way to get code-first's convenience without losing review of the API surface?Commit the generated document and add a CI step that regenerates it and fails when the committed file differs. Every contract change then appears as a reviewable diff in the pull request, so a renamed public field cannot ship unnoticed, and the committed spec can never go stale.
- Why do code-first specs tend to produce thinner documentation?Because prose lives in annotations, which are tedious to write and easy to skip, so `description`, `summary` and `example` fields end up sparse. Renderers show only what the document contains, so the published page degrades to a field list. The document also inherits the serialiser's representation choices rather than deliberate contract design.
saying these in an interview costs you the question
- Says contract-first prevents drift with no enforcement
- Treats a generated spec as reviewed because it is accurate
- Claims code-first cannot produce usable SDKs
- Thinks the choice is purely about developer preference
- Ignores that internal refactors become public contract changes