← Recipes

阅读中文版本

Catch Breaking OpenAPI Changes Before Release with oasdiff

Diff two OpenAPI documents with oasdiff in CI, fail the build on removed fields or narrowed types, and publish a human-readable changelog for review.

~25 minapiopenapici

Task

Compare the current OpenAPI spec against the released one in CI and block merges that remove or narrow public API surface.

When to use this

A refactor renamed a response field. Tests passed, the deploy was green, and three client SDKs broke in production within the hour.

Prerequisites

  • OpenAPI 3 documents generated in CI from the current build
  • The released spec accessible by URL or artifact
  • oasdiff available as a Docker image or binary in CI

Steps

1. Generate the current spec and download the released one.

curl -s https://spec.example.com/releases/openapi-v3.2.json -o released.json && npm run generate:openapi

Expected output: released.json contains the public contract and the fresh build writes the candidate spec to compare against it.

2. Run a breaking-change check locally first.

oasdiff breaking released.json openapi.json

Expected output: Removed response fields print with the exact location, for example response 200 field order.items was removed.

3. Get the exit code behavior right for CI.

oasdiff breaking released.json openapi.json --fail-on ERR --format text; echo "exit=$?"

Expected output: Error-level findings exit non-zero while warnings pass, which is the right default gate for a merge pipeline.

4. Wire it into CI with the Docker image.

- name: Breaking change gate
  run: |
    curl -s "$RELEASED_SPEC_URL" -o released.json
    docker run --rm -v "$PWD:/spec" redocly/oasdiff breaking \
      /spec/released.json /spec/openapi.json --fail-on ERR

Expected output: The pull request fails with a list of exact breaking changes instead of shipping them silently.

5. Also emit the changelog artifact for reviewers.

oasdiff changelog released.json openapi.json > api-changelog.md

Expected output: A markdown changelog lands in the build artifacts, so reviewers approve API changes consciously rather than by accident.

Troubleshooting

Problem

Diff reports noise on reordered fields

Solution

JSON key order is not semantic; ensure both specs are generated by the same toolchain version so ordering is stable, or sort keys before diffing.

Problem

Intentional breaking change is blocked

Solution

Version the API path or media type instead of weakening the gate; a deliberate break belongs in a new major version with its own spec baseline.

Problem

Spec generation differs between environments

Solution

Generate the spec inside CI in a controlled job rather than committing a hand-edited file, so the diff compares code truth against code truth.

Limitations

  • The tool compares documents, not behavior; a spec-identical change can still break clients through semantics such as new validation.
  • Undocumented endpoints are invisible to the diff, so the gate only protects what the spec actually describes.

The contract nobody was checking

Your OpenAPI document is a promise to every client you cannot see. Refactors rename response fields, tests stay green because the server is consistent with itself, and the break only appears in someone else build. Comparing the released spec against the freshly generated one makes that promise enforceable: removals and narrowed types fail the pipeline, and the changelog output turns invisible API edits into a reviewable artifact.

oasdiff breaking released.json openapi.json --fail-on ERR

# Also produce a reviewer-facing changelog
oasdiff changelog released.json openapi.json > api-changelog.md

Fail on errors, warn on the rest

Not every diff is equally severe: removing a response field is a hard error, adding an optional request parameter is usually a warning. Gate the pipeline on error-severity findings only, and route warnings into the changelog. That keeps the gate strict where clients break and quiet enough that people keep it enabled.

Baseline per major version

When a breaking change is genuinely intended, do not weaken the gate; start a new major version with a new baseline spec. The comparison then stays meaningful inside the version, and clients get a clean migration story instead of a silent field rename.