The idea
You maintain an HTTP API and you version your OpenAPI spec in git. Every pull request that touches the spec should automatically tell you whether the change is safe (adding an optional field, adding a new endpoint) or breaking (removing a field, changing a parameter type, tightening a required constraint). This tool is a standalone CLI that takes two spec files — or two git refs — and prints a structured diff classified by severity, then exits with a non-zero code if any breaking changes are present.
Why build this
API consumers — internal teams, mobile apps, third-party integrations — break silently when a contract changes without warning. Today the options are either expensive SaaS (Stoplight, Optic) or writing custom diffing logic per project. A small, composable CLI that does one thing well and drops into any CI pipeline is missing from the open-source landscape. The rise of generated clients (from OpenAPI specs via openapi-generator or oapi-codegen) has made this more urgent: a breaking spec change instantly breaks generated client code in downstream repos.
The tooling that does exist (openapi-diff, oasdiff) is either unmaintained, produces hard-to-parse output, or lacks a clean exit-code contract for CI use. There is a clear gap for something with a predictable interface and a tight JSON/SARIF output mode for GitHub Actions annotations.
Stack sketch
- Runtime: Go — single static binary, zero runtime deps, easy to install in CI with a
curl | shone-liner. - Spec parsing:
kin-openapi(Go) for loading and validating OpenAPI 3.x YAML/JSON. - Diffing engine: walk the parsed spec trees and compare path items, operation objects, schema objects recursively.
- Output formats: human-readable colored terminal output by default;
--format jsonfor machine consumption;--format sariffor GitHub Advanced Security annotations. - Git integration:
--base refs/heads/main --head HEADmode shells out togit showto read specs from two refs without requiring a checkout. - CI distribution: a GitHub Action wrapper (
uses: you/openapi-diff-action@v1) that wraps the binary download and wires up SARIF upload automatically.
Scope for v1
- Load two OpenAPI 3.x spec files (YAML or JSON) from disk or via
--base/--headgit refs. - Detect and classify the most common breaking changes: removed paths, removed operations, removed required request body fields, removed response fields that were previously present, type changes on existing fields, new required parameters added to existing operations.
- Classify safe changes: new optional fields, new paths, new operations, description-only edits.
- Print a summary table and exit 0 (no breaking changes) or 1 (breaking changes found).
--format jsonoutput with a stable schema so downstream tools can parse it.- Single binary distribution with install script and a Homebrew formula.
- Deliberately out of scope for v1: OpenAPI 2.x (Swagger) support, schema composition (
allOf/oneOf) deep diffing, custom rule configuration.
Where it could go
Once the core diffing is solid, the obvious expansion is a rule engine: teams have different tolerances for what counts as "breaking." A YAML config file (apibreak.yml) could let you promote certain changes from warning to error, or suppress known-intentional removals with an inline annotation in the spec. This is where Optic gets a lot of its value, but a local-first config file approach avoids the dependency on a cloud service.
The second expansion path is a watch mode that integrates with git bisect to find which commit introduced a breaking change in a historical range — useful when a downstream consumer starts failing and you need to pin the blame quickly.
Watch out for
OpenAPI schemas can be deeply recursive through $ref resolution, and a naive recursive diffing implementation will either stack-overflow on pathological specs or give false positives when the same schema is referenced from multiple paths. Invest in a proper $ref deref pass before diffing, and write property-based tests against a corpus of real-world open-source specs (the APIs-guru dataset is a good source) before calling v1 done.