Lesson 22 / 25

An API-First Workflow in CI/CD

Automate validation, breaking-change checks, docs and SDKs in a pipeline.

The contract as a build artefact

A mature API workflow treats the OpenAPI document like code. It lives in Git (in the service repository or a central API repository), split into files and bundled for publishing. Every pull request triggers: validation and linting (Spectral or Redocly) against the house style guide; a breaking-change check (oasdiff or similar) against the version on the main branch, requiring explicit approval or a major version bump for breaking changes; example validation so examples match their schemas; and, for implemented APIs, contract tests against a running build. After merge, the pipeline publishes documentation (Redoc or Swagger UI to the developer portal), generates and publishes SDKs with semantic versions, updates mock servers for consumers, and registers the API in a catalogue. Reviews include API consumers and an API design reviewer for public APIs. This turns documentation from a chore that drifts into the single source of truth that drives everything else.

The API contract pipeline

Every change to the contract is linted, diffed, tested and then published as docs, SDKs and mocks.

A horizontal pipeline of five connected stages, ending in three outputs branching to a book, a package and a mask icon.
Figure 8.1 — From pull request to published docs, SDKs and mocks.

A GitHub Actions workflow for an OpenAPI contract

Lint, check for breaking changes and bundle on every pull request.

name: api-contract
on:
  pull_request:
    paths: ["api/**"]

jobs:
  contract:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with: { fetch-depth: 0 }
      - uses: actions/setup-node@v4
        with: { node-version: 22 }
      - name: Lint
        run: npx @stoplight/spectral-cli lint api/openapi.yaml --fail-severity=error
      - name: Bundle
        run: npx @redocly/cli bundle api/openapi.yaml -o dist/openapi.yaml
      - name: Breaking-change check
        run: |
          git show origin/${{ github.base_ref }}:api/openapi.yaml > base.yaml
          docker run --rm -v "$PWD:/w" -w /w tufin/oasdiff breaking base.yaml api/openapi.yaml --fail-on ERR
      - uses: actions/upload-artifact@v4
        with: { name: openapi, path: dist/openapi.yaml }

Make breaking changes a conscious decision

A failing breaking-change check should not be bypassed silently. Require a label, an approval from the API owner and a version bump, so breaking changes happen on purpose and with a migration plan.

Quick check: In an API-first pipeline, what does a breaking-change check compare?

  • Two database schemas
  • Swagger UI themes
  • The new OpenAPI document against the previous published version
  • Server CPU usage
Answer

The new OpenAPI document against the previous published version — It diffs contracts to detect changes that would break existing clients.