Skip to content

Broker API Changelog Diffing Tool

broker-api-changelog-diffing-toolsource

Use before upgrading a broker SDK or OpenAPI spec, to diff two schema snapshots for removed endpoints, newly required parameters, enum mutations and type changes so CI fails before the change reaches an order path.

Version
3.0.0
Reading
5 min
Hands off to
4
Handed off from
4
License
Apache-2.0
CoversOpenAPI 3.xSwagger 2.0 (reference resolution only)

When to Use

Invoke this before upgrading a broker SDK version or pulling a new API specification (Binance, Coinbase, IBKR Client Portal and similar publish OpenAPI documents). Broker releases introduce silent breaking changes — a removed nested response field, a parameter that quietly became mandatory, a new order status the state machine has never seen. This skill diffs two schema snapshots and classifies what changed, so a CI job can fail the build before the change reaches an order path.

The tool is a gate, and its failure modes are asymmetric: a false positive costs a developer a few minutes, a false negative ships a broken integration. Everything about its classification is biased accordingly.

When NOT to Use

  • As proof a release is safe. It compares structure only. Rate limits, auth scope changes, altered matching-engine behaviour, changed rounding, new error codes returned in a 200 body — none are expressible in a schema, and none will appear in the report. A clean diff means "nothing structural broke", not "safe to deploy".
  • On specifications it cannot fully resolve. Only local (#/...) references are followed. External and remote $refs are reported as UNRESOLVED_REF — that region was not compared, and treating the report as complete when one is present is a mistake.
  • As a Swagger 2.0 differ. #/definitions/... references resolve, but Swagger 2.0's body parameters and top-level consumes/produces are not modeled; the request-body logic assumes OpenAPI 3.x requestBody.content. Convert 2.0 documents to 3.x first.
  • As a file loader. It takes parsed Python dictionaries. Reading and parsing JSON or YAML is the caller's job.
  • For composition keywords. oneOf, anyOf, allOf and discriminator are not evaluated; schemas using them will diff only at the level the tool can see.

Prerequisites

  • Baseline (older) and target (newer) API schemas, parsed into dictionaries.
  • Both documents complete, including the components/definitions sections the $refs point at — a spec split across files must be bundled first, or references will come back unresolved.

Workflow

  1. Load both documents and let the differ reject unusable input. diff_schemas raises SchemaDiffError when a document is not a mapping or has no paths. This is deliberate: a failed download or a wrong path yields an empty document, and a differ that shrugs and reports zero changes turns the gate green at exactly the moment it matters.

  2. Diff endpoints. Removed paths and removed methods are CRITICAL_BREAKING. Only real HTTP methods are treated as operations — a Path Item Object also legally carries parameters, servers, summary, description and $ref, and path-level parameters are diffed as shared across every operation.

  3. Resolve $ref before comparing anything. Real broker specs describe payloads almost entirely through references, and a $ref schema carries no type, properties or enum of its own. Resolution follows #/components/schemas/... and #/definitions/... against the document each side came from, with cycle protection for self-referential models.

  4. Treat absence as a change. A removed response status code, a removed request or response content type, and a removed requestBody are all breaking and all invisible to a differ that walks only the keys present on both sides.

  5. Check requirement transitions in both directions. A request field or parameter moving into required breaks callers that omit it. A response field moving out of required breaks parsers that assume it is present. Both matter; they are not the same check.

  6. Classify enums by direction. A request enum constrains what the client may send, so removing a value is breaking. A response enum constrains what the client must handle, so adding a value is breaking — a new order status silently breaks an exhaustive state machine. Newly imposing a request constraint, and dropping a response constraint, are breaking too.

  7. Gate the build. report.exit_code is 0 when compatible and 1 otherwise; report.format_report() renders the findings severity-first. is_compatible is False if any change is MEDIUM_BREAKING or higher.

Full procedure: see references/workflows.md. Severity matrix and classification rationale: see references/standards.md. Printable pre-flight checklist: see assets/checklist.md.

Common Pitfalls

  • Comparing $ref schemas without resolving them. Both sides look like empty objects, every check is skipped, and a release that deleted an entire response model reports clean. This is the single most likely way to get a false green.
  • Ignoring an UNRESOLVED_REF finding. It is informational in severity but it means a region of the schema was never compared. Bundle the spec and re-run.
  • Only diffing keys present on both sides. Removals are the breaking changes; a loop written as if key in new: compare(...) cannot see any of them.
  • Treating enum changes as direction-agnostic. Flagging every set difference raises false alarms on request widenings while missing the response additions that actually break consumers.
  • Assuming a scalar type. OpenAPI 3.1 allows type: ["object", "null"] where 3.0 used nullable: true. An equality test against the literal "object" silently skips property diffing, and comparing the two spellings reports a mutation that never happened.
  • Treating every key under a path as an HTTP method. parameters is a list and summary is a string; calling .get() on them raises on a perfectly valid document.
  • Letting an empty or malformed document produce a clean report.
  • Unbounded recursion on self-referential models. Order.parent → Order is ordinary, and resolving references without a cycle guard hangs the build.
  • Reading a clean report as deployment approval. Structure is not behaviour.

Verification

  • Run the unit suite and confirm every test passes: python -m unittest discover -s skills/broker-api-changelog-diffing-tool/scripts
  • Build a fixture whose response model sits behind a $ref, delete a field from the referenced component, and confirm REMOVED_RESPONSE_FIELD is reported. A differ that passes every inline-schema test can still fail this one, which is the case that matters.
  • Confirm two empty documents raise SchemaDiffError rather than reporting compatible.
  • Confirm a self-referential model terminates.
  • Confirm direction-aware enum behaviour: adding a response enum value is breaking; adding a request enum value is not.
  • Confirm a Path Item Object carrying parameters and summary does not raise.
  • Mutate fixtures by deep-copying a baseline and changing exactly one thing, so a test for one change cannot accidentally introduce another.

Verify it, from the repository root

python -m unittest discover -s skills/broker-api-changelog-diffing-tool/scripts

Hands off to 4

Skills this document names, usually in When NOT to Use, as the owner of a case it excludes.

Handed off from 4

Skills that name this one as the place a case belongs. The reverse edges of the graph.