The 30-Second Check That Catches API Changes Before They Break Production

Schema Breach: The 30-second check that catches API changes before they break production

A junior engineer added a required field to a POST /v1/orders endpoint last Tuesday. The change was six lines of YAML, the OpenAPI validator passed, the unit tests passed, and the change shipped in the 4 p.m. deploy. By Thursday morning the customer support Slack had 38 messages in it, all variations on “our integration has been throwing 422s since yesterday afternoon.” The field was currency_code — the team had decided to require it explicitly instead of defaulting to USD — and every existing caller was still sending the old payload, which the new schema now rejected. The fix was a 30-line code change; the recovery took two engineers a full day, plus a customer call. None of that needed to happen, and the entire regression would have been caught by a 30-second schema diff run against the previous version of the spec. The OpenAPI Diff Breach Detector is the tool that runs that diff, classifies each change as breaking, dangerous, or non-breaking, and prints an impact-oriented report a human can read in the PR review step. For a fuller look at the contract-testing workflow, the Elysia Tools developer hub collects the diff detector alongside a code-snippet generator, a response validator, and a JSON Schema generator that all speak the same vocabulary.

The first time you ship a breaking change without noticing, you find out from a customer. They were sending a request your new spec considers invalid, your API now rejects it, and their integration goes dark. You did nothing wrong technically; the schema was just “tighter” in a way that downstream code couldn’t anticipate. The pattern repeats across teams because most diff tools only check syntactic differences, not semantic ones, and the distinction matters more than the syntax. A new optional field in a request body is a syntactic change but semantically non-breaking. A removed response field is syntactically a one-line diff but semantically breaks every consumer that reads it. A parameter going from optional to required looks like a tiny change in the YAML but instantly turns every old client into a broken client. The OpenAPI Diff Breach Detector (Elysia Tools) sorts these into three buckets — breaking, dangerous, and non-breaking — using rules that match the standard guidance for API evolution. You see the path, the operation, and a one-sentence impact summary, so the reviewer reading the PR can decide whether to ship, soften, or split.

The 3 severities, and why dangerous is its own bucket

The first time most people build a diff tool, they make it a binary: breaking or not. The detector instead recognizes a third category: dangerous changes that aren’t strictly breaking but that almost always cause problems downstream. A response field whose type changed from string! to string (non-null to nullable in GraphQL terms) is the canonical example. Nothing on the client side will fail to compile or validate, but code that relied on a non-null value can crash at runtime if the field is now occasionally absent. The detector flags it as dangerous, not breaking, so it doesn’t block a routine release, but the report still surfaces it for review. The same applies to removing a non-2xx response code from an operation definition; the success path still works, but error-handling branches that depended on that status will go cold. These are the changes that survive a CI pass and bite you six months later, and having them visibly named in the report changes how teams talk about API evolution.

Severity 01: Breaking — Removed endpoint, required parameter, removed response field, or schema mismatch. releaseRisk: high.

The breaking category is the easy one: removed endpoints, removed required parameters, removed response fields, and schema-fingerprint mismatches on existing fields. Each of these will produce a hard failure on a client that hasn’t been updated. The non-breaking bucket covers additive changes: new endpoints, new optional parameters, new optional response fields, and new response status codes. These are safe to ship without coordinating with consumers, though they still need a changelog entry so SDK authors and integration teams can pick them up. The interesting work the detector does is keeping the dangerous category separate, because a binary “is this safe” answer is too crude for the real shape of API change.

What the report actually shows

The output is a structured JSON object, but the part that matters is the changes array, where each entry has a path, a severity, an area, a one-line summary, and an optional impact sentence when “Include Impact Analysis” is checked. The area field is the meta-classification — endpoint, parameter, request-body, response, or schema — and it groups changes by where in the API definition they occurred, so a reviewer’s eye can skim the report and tell at a glance whether the breaking changes cluster around the inputs, the outputs, or the routes themselves. The releaseRisk field at the top of the report is the headline: high if any breaking changes are present, medium if only dangerous ones, and low if the diff is purely additive. That single label is what belongs in the PR summary; the full array is what the engineer reads before approving.

Severity 02: Dangerous — Field went nullable, response status removed, type loosened but still valid. Passes validation, breaks at runtime. releaseRisk: medium.

The path field gives you enough to navigate the change back to the original spec. For a parameter change it reads POST /v1/users -> header:X-Request-Id. For a request-body field change it reads PATCH /v1/users/{id} -> request.email. For a response change it reads GET /v1/users/{id} -> response 200.id. The paths aren’t always as long as that, but they always tell you which operation and which field, so the report survives being pasted into Slack or a Jira ticket without losing context. This is the detail that makes the tool useful in code review, not just in CI: a reviewer can read the path, hop to the diff, and decide in seconds whether the change is acceptable.

A representative real-world diff illustrates the report shape. Imagine a team’s old POST /v1/orders schema requires only customer_id and items[], and the new version adds currency_code as a required field and removes the legacy_sku field from the request body. The detector’s report for that diff would look roughly like this in the changes array:

{ severity: "breaking", area: "request-body", path: "POST /v1/orders -> request.currency_code", summary: "Required request field added", impact: "Existing request payloads missing this field must be updated before release." }
{ severity: "breaking", area: "request-body", path: "POST /v1/orders -> request.legacy_sku", summary: "Request field removed", impact: "Clients sending this field may lose behavior or fail validation if downstream logic depended on it." }

The headline releaseRisk: "high" is what the developer copy-pastes into the PR description; the two breaking-change entries are what reviewers read before approving. Both lines of the report trace cleanly back to the YAML diff, so a reviewer can navigate from the report to the spec without leaving the PR.

The auto-detect on OpenAPI vs GraphQL

The detector accepts both OpenAPI and GraphQL schemas in the same input, which is unusual. Most diff tools only understand one, and the reason is that the underlying models are very different: OpenAPI is path-and-operation based, GraphQL is type-and-field based, and the kinds of changes that break one don’t always break the other. A removed path in OpenAPI is breaking. A removed type in GraphQL is also breaking, but the kind of thing that breaks is a query or fragment, not a request to a specific URL. The detector handles both by detecting the schema kind from the input — looking for openapi: or swagger: to identify OpenAPI, and type Query or schema { to identify GraphQL — and then routing to the appropriate comparison function. The schemaType option lets you override the auto-detect when a schema is ambiguous, but for the typical case you can leave it on “Auto detect” and trust the result.

In GraphQL terms, the dangerous category does more work than in OpenAPI. A field whose type changed from non-null to nullable is one of the most common GraphQL changes — teams relax nullability as the schema matures — and the detector’s “dangerous” bucket is exactly where that change lands. Required input field additions (a new !-typed field on an input type) are classified as breaking rather than non-breaking, because every mutation sending that input object would now fail validation. These distinctions are exactly the ones the GraphQL spec working group has spent years arguing about, and seeing them encoded in a diff tool saves every API team from re-deriving them from first principles. The same logic underpins the OpenAPI side, where a “required request field added” matches the same risk profile as a GraphQL “required input field added”: existing callers are now sending an incomplete payload, and the server’s validation will reject them.

Where this fits in the release pipeline

The natural place for this check is in the pull-request workflow, between the moment the schema changes are merged into the spec file and the moment the code change is approved. Concretely, the typical setup is: a developer updates the openapi.yaml in their feature branch, opens a PR, and the CI step runs the detector with the new file as newSchema and the main-branch version of the file as oldSchema. The CI step prints the report to the PR as a comment, and the maintainer reads the releaseRisk label and the breaking-change list before approving. If the diff is high-risk, the PR is paused for a manual review of the migration plan; if it’s low-risk, the merge proceeds and the changelog gets a new entry. The detector doesn’t try to do all of this; it just provides the input the human needs to make a defensible decision in 30 seconds instead of 20 minutes.

Severity 03: Non-breaking — New endpoint, optional parameter, optional response field, new status code. Existing clients keep working. releaseRisk: low.

What it doesn’t do is also worth naming. It doesn’t generate client SDKs, doesn’t validate that an actual response matches a schema, doesn’t enforce a versioning policy, and doesn’t open a PR for the migration. Each of those is a separate tool, and the Elysia Tools developer hub covers the rest of the contract-testing workflow with a code-snippet generator for client requests, a response validator for live APIs, and a JSON Schema generator for synthetic test data. The detector’s job is narrower and more focused: tell you, in the format of a code review, whether the schema change you’re about to ship will break someone. The 30 seconds it takes to read that report is the cheapest insurance an API team can buy — and the question worth sitting with is what your team’s last silent regression actually cost. The two days of recovery in the Slack thread above were about as cheap as it ever gets, and most teams will never measure that number at all. The ones that start measuring it tend to be the ones that adopt tools like this within a quarter.

Comments

No comments yet. Why don’t you start the discussion?

    Leave a Reply

    Your email address will not be published. Required fields are marked *