{"slug": "my-openapi-differ-flagged-a-breaking-change-that-broke-nobody-the-enum-shrank-on", "title": "My OpenAPI differ flagged a breaking change that broke nobody — the enum shrank on the response side", "summary": "A developer built an OpenAPI changelog generator that diffs two specs and outputs structured breaking-change lists without LLM rewriting, and its first dogfood run flagged a single breaking change — a response-side enum shrinking from three values to two — that had already shipped without breaking anything. The incident led to a direction-aware differ that treats the same textual delta differently depending on whether the schema sits on the request or response side, plus spec normalization (expanding $refs, sorting keys, canonicalizing) to eliminate false diffs from property ordering. The tool is packaged as the OpenAPI Changelog Generator, taking a base spec URL and a new spec and returning a human-readable changelog alongside a machine-readable change list.", "body_md": "I built an OpenAPI changelog generator because writing API release notes was eating my afternoons: diff two specs, output a structured list of breaking changes, no LLM rewriting history. First real dogfood run — comparing my own API's v3 spec against v2 — it flagged exactly one breaking change. Except that change had shipped a week earlier and nothing broke.\n\nThe delta: a `status` field's enum went from `[\"queued\",\"shipped\",\"failed\"]` to `[\"queued\",\"shipped\"]`. Textbook breaking change, the differ said. But that field lived in a **response** schema. A server returning fewer enum values cannot surprise a client that already handles all three — the client's switch statement still compiles. The server promised less variety, not less data.\n\nThat's when it clicked: breaking-ness has a direction, and the same textual delta flips meaning depending on which way the schema points.\n\n`number` to `integer` — all breaking. Old clients start sending rejected payloads.\nThe inverse bit me earlier too: adding a value to a response enum means old clients suddenly receive data their validators reject. Same operation, opposite verdict, depending on the boundary side.\n\nSecond fix, less glamorous: I now normalize specs before diffing — expand `$ref` s, sort keys, canonicalize. Without that, two semantically identical specs whose properties happened to be ordered differently produced a wall of fake diffs. JSON comparison is not semantic comparison.\n\nThe direction-aware version now gates my own spec releases before anything ships. I eventually packaged it as the [OpenAPI Changelog Generator](https://x402.freeq.one/tools/changelog_openapi.html) — it takes a base spec URL plus the new spec and returns a human-readable changelog alongside a machine-readable change list.\n\nIf you diff specs with a naive property-level tool, check which side of the boundary the changed schema sits on. It flips the verdict more often than you'd expect.", "url": "https://wpnews.pro/news/my-openapi-differ-flagged-a-breaking-change-that-broke-nobody-the-enum-shrank-on", "canonical_source": "https://dev.to/imapphelp/my-openapi-differ-flagged-a-breaking-change-that-broke-nobody-the-enum-shrank-on-the-response-side-5b7c", "published_at": "2026-10-09 03:43:50+00:00", "updated_at": "2026-10-09 03:47:54.002961+00:00", "lang": "en", "topics": ["developer-tools", "ai-tools"], "entities": ["OpenAPI Changelog Generator"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/my-openapi-differ-flagged-a-breaking-change-that-broke-nobody-the-enum-shrank-on", "markdown": "https://wpnews.pro/news/my-openapi-differ-flagged-a-breaking-change-that-broke-nobody-the-enum-shrank-on.md", "text": "https://wpnews.pro/news/my-openapi-differ-flagged-a-breaking-change-that-broke-nobody-the-enum-shrank-on.txt", "jsonld": "https://wpnews.pro/news/my-openapi-differ-flagged-a-breaking-change-that-broke-nobody-the-enum-shrank-on.jsonld"}}