Make OpenAPI the single contract in your repo
Keep openapi.yaml at the service root, next to /src/*. Add a CODEOWNERS rule for /openapi.*, so every contract edit lands through PR review by the API owner.
Define the contract
Treat OpenAPI as the source of truth. Handlers implement the paths, methods, parameters, request bodies, responses, schemas, and examples declared in the spec (source: OpenAPI Specification 3.1.0 — OpenAPI Initiative (2021)). Delete wiki-only endpoints and undocumented query parameters.
Make examples executable
Give every path request and response schemas plus realistic examples. Use real field shapes, valid enum values, and plausible error payloads; these examples become contract-test fixtures later.
Generate docs from CI output
Build the docs site from the same openapi.yaml artifact produced in CI. Keep /docs/* generated, and avoid manual copy-paste between handlers, Markdown, and docs pages.
Use a small repo layout that keeps the contract, implementation, generated docs, workflow, and ownership rule visible together.
/openapi.yaml
/src/*
/docs/* # generated
/.github/workflows/api-docs.yml
/CODEOWNERS
Example CODEOWNERS entry: /openapi.* @api-owners.
CI pipeline: 4 checks that block drift before merge
Lint and validate
Run the Spectral lint command in CI against openapi.yaml and .spectral.yaml. Treat Spectral errors as merge blockers; the CLI is designed to lint OpenAPI documents against a ruleset (source: Stoplight Spectral docs (consulted 2026-06)).
Run contract tests
Start the dev server, wait until it is reachable, then run Dredd against openapi.yaml and the local service URL. Dredd is used to compare documented API behavior with HTTP responses from the service (source: Dredd documentation (consulted 2026-06)).
Detect breaking spec changes
Compare openapi.yaml in the pull request against origin/main. Fail the job when a path or response field disappears without a version bump, and label the result as non-backward compatible.
Smoke-test docs generation
Build the docs artifact from openapi.yaml, then run the link checker used by the docs site. Fail on generator errors or broken links, because both mean the published contract cannot be trusted.
Use separate GitHub Actions jobs so branch protection can require each check by name. pull_request workflows run on PR activity, and required status checks can block merging until selected jobs pass (source: GitHub Actions Documentation (consulted 2026-06)).
name: api-contract
on: [pull_request]
jobs:
spectral:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npx @stoplight/spectral lint openapi.yaml --ruleset .spectral.yaml
dredd:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm run dev &
- run: npx dredd openapi.yaml http://127.0.0.1:3000
spec-diff:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- run: npm run spec:diff -- openapi.yaml origin/main:openapi.yaml
docs-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm run docs:build && npm run docs:check-links
Versioning and deprecation in the spec (not the wiki)
info.version is the API contract version, not the docs site version. In openapi.yaml, bump it only when clients must change code: removed operation, renamed field, stricter required input, or changed response shape. OpenAPI defines info.version as required Info Object metadata (source: OpenAPI Specification 3.1.0 — OpenAPI Initiative (2021)).
| Decision | Spec location |
|---|---|
| Path-based versioning | `servers` and `paths`, for example `/v1` on every route |
| Header-based versioning | A required `Accept` or `Version` header parameter on every versioned operation |
| Deprecated operation or field | `deprecated: true` plus a removal date in `description` |
Set deprecated: true in the spec before merging code that emits the old shape. Put the removal date in the same description, so generated docs and SDK warnings carry the deadline without a wiki lookup.
A deprecation PR should update the relevant parts of openapi.yaml: deprecated: true, a removal-date sentence, a changelog note in info.description, and a response header such as Deprecation under responses.*.headers for runtime visibility.
Pick either path-based or header-based versioning once. Do not mix /v1 routes with header-only versioning unless the spec models both consistently in servers, paths, parameters, and responses.
Examples, SDKs, and docs from one spec artifact
Treat openapi.yaml as the build input for consumer-facing artifacts: executable examples, SDK packages, and the static docs site.
Test examples against the service
Keep request and response examples realistic: use valid IDs, enum values, error bodies, and pagination shapes that your service can return. Run Dredd in CI against the deployed test service; it compares the API description with real HTTP responses and helps catch drift in documented behavior (source: Dredd documentation (consulted 2026-06)).
Generate SDKs, then compile them
Generate SDKs from the same file with openapi-generator-cli generate -i openapi.yaml -g typescript-fetch -o build/sdk/typescript. Then run the SDK compiler or package test command as a smoke test, so invalid schemas, renamed fields, and incompatible operation signatures fail before merge (source: OpenAPI Generator docs (consulted 2026-06)).
Build docs from the tested spec
Publish the docs site from the tested openapi.yaml. Do not copy endpoint descriptions into a CMS page or README fork.
Version and attach release artifacts
On release, tag the spec commit, then attach generated SDK packages and the docs build output to that release. Consumers can trace every SDK and docs page back to the contract that produced it.
Rollout checklist and a paste‑in template pipeline
Seed the repo
Commit openapi.yaml, .spectral.yaml, and .github/CODEOWNERS. Put /openapi.* under API owner review. Keep the Spectral ruleset minimal so the first rollout finds real drift, not style noise (source: Stoplight Spectral docs (consulted 2026-06)).
Start soft, then enforce
Run the CI job as non-blocking for the first rollout with continue-on-error. After flaky checks have owners and fixes, remove that flag and mark the job required in branch protection (source: GitHub Actions Documentation (consulted 2026-06)).
Backfill examples
Use access logs to choose high-traffic endpoints. Add request and response examples for those paths, then extend coverage weekly until every critical flow has examples.
Remove duplicate docs
Point docs hosting at the CI-built artifact. Delete wiki pages that duplicate paths, parameters, responses, or examples already present in openapi.yaml.
Add this standing PR-template line: If this changes the API, update openapi.yaml and examples.
# .github/workflows/openapi.yml
name: openapi
on: [pull_request]
jobs:
contract:
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npx spectral lint openapi.yaml
- run: npm run openapi:diff
- run: npm run contract:test
- run: npm run docs:build
- uses: actions/upload-artifact@v4
with:
name: openapi-docs
path: dist/docs
Continue reading
How to Choose a Kapa.ai Alternative: A Practical Playbook
A founder-grade workflow with pricing math and test scripts to pick a Kapa.ai alternative in one sprint.
Top Zendesk AI Alternatives for Customer Support: Clear Picks
A developer‑first shortlist of Zendesk AI alternatives with concrete use cases, pricing signals, integration notes, and a low‑risk 10‑day migration checklist.
Engineering Velocity Improvements Without Scoring: Playbook
A practical, no‑leaderboard approach to accelerate delivery by removing wait states, shrinking change size, and tightening CI/CD feedback using defaults in common dev tools.