OpenAPI Diff: Compare Two Specs and Catch Breaking Changes
Free OpenAPI diff tool — compare two spec versions and see exactly which changes are breaking vs. safe, with the reason for each. Runs client-side.
Try it now: OpenAPI Diff Checker — Compare two OpenAPI versions and see which changes actually break existing clients, with the reason for every verdict spelled out.
A Textual Diff of Two Spec Versions Tells You Almost Nothing
Run openapi-v1.yaml and openapi-v2.yamlthrough a plain text or line diff and you'll get pages of noise before you reach a single change that matters. Operations get reordered as endpoints are added. A linter reformats indentation. A schema gets pulled out into components and referenced instead of inlined, which rewrites half the document without changing what a single field actually accepts. None of that is a real change to the contract a client depends on — it's exactly the same key-order-and-whitespace problem GenKitLab's JSON Diff tool solves generically for any JSON document, by parsing both sides and comparing values instead of bytes.
A structural diff of an OpenAPI document gets you past the whitespace, but it still isn't enough on its own. Once reordering and formatting are out of the way, what's left is a flat list of “this path changed” entries — components.schemas.User.properties.email now has a different type, paths./orders.get.responses.200 lost a property. That list is accurate, but it hands the actual judgment call — does this break anyone?— back to whoever is reading it. An openapi diff worth building has to go one level deeper than swagger diff tooling that just flags “X changed”: it has to know what each kind of change means for a client that was already integrated against the old version.
Not Every Spec Change Is Breaking
This is the point the rest of this guide is built around: most changes to an OpenAPI spec are backward compatible, and only a specific subset actually breaks existing clients.A breaking change detector earns its name by telling those two categories apart automatically, rather than reporting a difference and letting a human decide whether it's safe.
Adding a new optional request field, a new endpoint, or a new optional query parameter is safe by construction. An existing client sends the same request it always did; it simply doesn't know the new field or parameter exists, and the server accepts the request exactly as before. Nothing about the client's behavior needs to change for the API to keep working.
# v1
properties:
id: { type: string }
email: { type: string }
# v2
properties:
id: { type: string }
email: { type: string }
phone: { type: string } # new, not in "required"
→ Non-breaking: existing clients never send "phone" and were never
required to. Requests built against v1 are still valid against v2.Compare that with a field that moves from optional to required, or disappears from a response entirely. A client built against the old contract has no reason to send that field, or was reading it straight off the response — and now either the request it always sent is rejected, or the field it was reading is gone. Removing a response field a client might depend on, changing a field's type, promoting an optional field to required, deleting an endpoint outright, or narrowing an enum's allowed values are all the same kind of problem: each one can silently break a client that did nothing wrong, because it was correctly relying on the contract as it stood before.
# v1
properties:
id: { type: string }
email: { type: string }
required: [id]
# v2
properties:
id: { type: string }
email: { type: string }
required: [id, email] # email is now mandatory
→ Breaking: any v1 client that omits "email" in a request — which
was perfectly valid before — now fails validation on v2.The point isn't that either change is wrong to make. It's that a tool built to compare openapi versions has to classify every change it finds as one or the other, and say why — “email moved from optional to required, so requests that previously omitted it now fail” — instead of listing required changed and moving on.
The Full List of What Actually Breaks a Client
- A response field is removed. Any client reading that field gets
undefinedinstead of the value it expects, with no error raised — often the quietest kind of breakage there is. - A field's type changes. A number becoming a string, or an object becoming an array, breaks deserialization in a statically typed client immediately and produces subtly wrong values in a loosely typed one.
- An optional field or parameter becomes required. Every existing caller that omitted it — legitimately, under the old contract — starts failing validation.
- An endpoint or an HTTP method on it is removed. Every client calling it gets a 404 or 405 where it previously got a 200.
- An enum's allowed values are narrowed. A value a client was legitimately sending or receiving under the old spec is no longer valid under the new one.
- A required security scheme is added or a base path changes. Both invalidate requests that were previously well-formed, even though nothing about the request body changed at all.
Everything else — new optional fields, new endpoints, new optional parameters, a new enum value added rather than removed, an added response field — is additive. A client that predates the change keeps working without modification, which is exactly what backward compatible is supposed to mean.
Gating a Release on the Verdict, Not the Reading
The practical payoff of classifying every change instead of just listing it is that api versioning stops being a policy someone has to remember and becomes a check that runs itself. “Bump the major version whenever you ship a breaking change” is easy to write in a contributing guide and easy to forget under deadline pressure — especially when the breaking change is small, like one field quietly becoming required in a schema three levels deep in the spec.
A CI step that runs a breaking change detector against the spec on mainversus the spec in the current pull request turns that policy into an automatic gate: fail the build if any change is classified as breaking and the version number wasn't bumped accordingly. The check doesn't need to understand the API's domain to do this — it only needs the classification rules above, applied consistently, every time a spec changes. That's a fundamentally more reliable enforcement mechanism than a reviewer scanning a 2,000-line spec diff for the one required: line that matters.
GenKitLab's OpenAPI Diff compares two OpenAPI versions and classifies every detected change as breaking or non-breaking, with the specific reason spelled out for each verdict — not just that required changed, but what that means for a client that already integrated against the old spec. It runs entirely in your browser: both spec files are parsed and compared client-side, and neither one is ever uploaded anywhere.
Diff After the Spec Is Valid, Not Instead of Validating It
A diff assumes both inputs already parse and make structural sense — it can tell you that a field moved from optional to required, but it can't tell you that the spec you just pasted is missing a required info.versionfield or references a schema that doesn't exist. That's a separate, earlier question, and it's the one covered in the OpenAPI Validator guide. Validate each version of a spec on its own with GenKitLab's OpenAPI Validator first, then diff the two valid versions against each other — in that order, a failure in the diff step always means a real contract change, never a spec that was malformed to begin with.
Frequently asked questions
›What is an OpenAPI diff and how is it different from a normal text diff?
An OpenAPI diff parses both spec versions and compares them structurally — by path, operation, and schema — rather than line by line, so reordered operations, reformatted YAML, and inlined-vs-referenced schemas don't show up as noise. A useful one goes further still: it classifies each real change it finds as breaking or non-breaking for existing clients, rather than just reporting that something is different.
›Is adding a new field to an OpenAPI spec a breaking change?
It depends which side it's added to. A new optional request field, a new optional query parameter, or a new endpoint is non-breaking — an existing client simply doesn't send it and keeps working exactly as before. A new field only becomes a problem if it's added to a response and a client's strict schema validation rejects unknown properties, which is uncommon, or if it's marked required on the request side.
›What OpenAPI spec changes actually break existing clients?
Removing a response field a client might read, changing a field's type, promoting an optional field or parameter to required, removing an endpoint or HTTP method, narrowing an enum's allowed values, and adding a previously-absent required security scheme are all breaking. Each one invalidates a request or response that was valid and working under the previous version of the contract.
›How do I compare two versions of an OpenAPI spec?
Paste or upload the old and new spec files into an OpenAPI diff tool that parses both (YAML or JSON) and compares them by path rather than by text. Look specifically for a tool that labels each change breaking or non-breaking with a reason, since a raw list of 'this changed' still requires a human to work out which changes are actually dangerous.
›Can a breaking change detector be enforced automatically in CI?
Yes — run the diff between the spec on the target branch and the spec in the pull request as a CI step, and fail the build if a breaking change is detected without a corresponding major version bump. That turns API versioning discipline from a guideline a reviewer has to remember into a check that runs on every pull request without exception.
›Should I validate an OpenAPI spec before diffing two versions of it?
Yes. A diff assumes both documents are structurally sound OpenAPI and only reports changes between them — it won't catch a spec that's individually malformed, like a missing required field or a reference to a schema that doesn't exist. Validate each version on its own first, then diff the two valid versions against each other.
Last updated