>_ Analyst Engineering

API Versioning and Breaking Changes: How Analysts Assess Impact Before a Release

Written by Ahmed at Analyst Engineering, a Senior Technical Business Analyst with 10+ years in banking and payments delivery.

Cover for API versioning and breaking changes, showing a change table marking field removal and tightened validation as breaking, with Deprecation and Sunset headers.

Key takeaways

  • A breaking API change is any change that makes an existing, correctly built consumer fail or behave wrongly without changing its own code, and it includes behavior changes that no schema diff can see.
  • Adding an optional request field is usually safe; adding a required one, removing or renaming a field, changing a type, or tightening validation are breaking; adding an enum value to a response is breaking for any consumer that rejects unknown values.
  • Stripe pins each account to a date-named API version that a request can override with the Stripe-Version header, and GitHub versions its REST API with the X-GitHub-Api-Version header, so breaking changes reach consumers only when they opt in.
  • The Deprecation response header, standardized in RFC 9745 in March 2025, announces when an endpoint is or will be deprecated, and the Sunset header from RFC 8594 announces when it will stop responding; the sunset must not precede the deprecation.
  • Yesterday's API test collection is today's compatibility test: rerunning an unchanged consumer-view collection against the new version catches the behavioral breaks that contract diff tools such as oasdiff cannot.

A breaking API change is any change that makes an existing, correctly built consumer fail without touching its own code. Some are obvious, like removing a field. The expensive ones are not: a tightened length limit, a new enum value, a changed default, or an operation that quietly became asynchronous. Assessing that impact before release is analysis work, and it needs both a contract diff and a behavioral check.

To manage API changes safely: classify each change as breaking, non-breaking, or “breaking for some consumers”; ship breaking changes only behind a new version consumers opt into; announce removals with Deprecation and Sunset headers plus a migration guide; detect contract breaks automatically with a tool such as oasdiff; rerun the existing test collection against the new version to catch behavioral breaks; and assess each consumer’s impact from real usage data before setting dates.

APIs for Analysts, advanced track. Builds on Part 4, how to document an API and contract testing. Full learning path: APIs for Analysts.

The running example is a real-world class of change from payments: aligning an API’s field lengths with ISO 20022 message limits. It looks like a small validation tweak and it is a breaking change for every consumer that sends long names today. Writing the changelog and deprecation notes consumers actually read is part of API Documentation from Scratch.

What counts as a breaking API change?

Use a consumer’s point of view: would an existing client, built correctly against the previous contract, now fail or produce wrong results? If yes, it breaks.

ChangeClassificationWhy
Add an optional request fieldNon-breakingExisting requests remain valid
Add a required request fieldBreakingExisting requests are now rejected
Add a response fieldUsually non-breakingBreaks only strict consumers that reject unknown fields
Remove or rename a response fieldBreakingConsumers reading it fail or get nothing
Change a field’s typeBreaking"1250.00" to 1250.00 breaks parsing and precision
Change a formatBreakingEpoch seconds to ISO 8601, or a date to a timestamp
Tighten validationBreakingmaxLength 140 to 70 rejects requests that passed yesterday
Loosen request validationUsually non-breaking for the APIBut may break downstream: longer values now flow into systems with shorter limits
Add an enum value in a responseBreaking for manyCode that handles known statuses mishandles the new one
Add an enum value in a requestNon-breakingExisting values still accepted
Change a defaultBreakingDefault page size 50 to 20 silently drops records from consumers that never paged
Change a status codeBreaking200 to 201, or 200 to 202, changes consumer logic
Change error codesBreakingConsumers switch on codes to decide retries and messages
Synchronous to asynchronousBreakingThe response no longer carries the outcome
Stricter rate limits or smaller payload limitsBreaking in practiceConsumers built within the old limits start failing
New required scopeBreakingExisting tokens receive 403
Same schema, different behaviorBreaking, invisible to diffsRounding, sorting, matching, or business rules changed

Two rows deserve emphasis because they are routinely missed.

Enum values in responses. Adding a status such as PDNG to a payment status enum looks additive. But a consumer whose code maps each known status to a screen label, a ledger entry, or a customer message has no path for PDNG. Depending on how it was built, it crashes, shows nothing, or falls into a default branch that treats the payment as failed. That is why API documentation should tell consumers to tolerate unknown values, and why the change log must still flag the addition.

Same schema, different behavior. No contract changed, every field is identical, and the API now rounds amounts differently or matches customer names more loosely. Only tests that assert business outcomes catch this.

Which API versioning strategies exist?

StrategyExampleStrengthsWeaknesses
URL path/v1/payments, /v2/paymentsObvious, easy to route and logCoarse: whole API versions move together
Header, date-basedStripe-Version, X-GitHub-Api-Version: 2022-11-28Fine-grained; consumers opt in by dateLess visible; needs good documentation
Account pinningStripe pins each account to a versionConsumers are protected by defaultOld versions must be maintained for a long time
Query parameter?api-version=2026-09-01Easy to try in a browserEasy to omit; muddles caching
Media typeAccept: application/vnd.example.v2+jsonPrecise per representationHarder to test and explain
No versions, additive evolutionCommon in GraphQL, with @deprecated fieldsNo version sprawlRequires strict discipline; breaking changes are nearly impossible

Stripe and GitHub show the date-based approach at scale. Stripe pins every account to the API version current when it started and lets a request override it with the Stripe-Version header, so a breaking change reaches a consumer only when they choose to upgrade. GitHub publishes dated REST API versions requested through the X-GitHub-Api-Version header. Neither is “the right answer”; the analyst’s question is whether the chosen strategy matches how often the API changes and how many consumers it has.

How should an API be deprecated?

Deprecation is a communication process with a technical signal attached.

  1. Announce in the changelog and directly to known consumers, with the reason, the replacement, and dates.

  2. Signal in responses. The Deprecation header, standardized in RFC 9745 (March 2025), carries a date indicating when the resource is or will be deprecated. The Sunset header from RFC 8594 carries the date it will stop responding, and must not be earlier than the deprecation date. A Link header can point to the migration guide.

    HTTP/1.1 200 OK
    Deprecation: @1798761600
    Sunset: Sat, 01 Jan 2028 00:00:00 GMT
    Link: <https://developer.example.com/migrate/v2>; rel="deprecation"; type="text/html"
  3. Measure usage. Gateway logs by client ID show who still calls the old version, how often, and which endpoints. Dates set without usage data get moved, repeatedly.

  4. Publish a migration guide with a field-by-field mapping from old to new, and a sandbox where the new version can be tested.

  5. Run brownouts close to the sunset: short, scheduled periods when the deprecated version returns errors, so forgotten consumers surface while there is still time.

  6. Remove on the published date, or publish a new date with a reason. Silent extensions teach consumers to ignore deprecations.

For an analyst, the deprecation plan is a mini-programme: stakeholders, communications, dates, a tracker of consumers and their migration status, and a go or no-go decision at sunset.

How do you detect breaking changes automatically?

Diff the contract. oasdiff compares two OpenAPI files and reports breaking changes:

oasdiff breaking openapi-v3.2.yaml openapi-v3.3.yaml

# In a pipeline: exit with code 1 if error-level breaking changes exist
oasdiff breaking --fail-on ERR openapi-main.yaml openapi-branch.yaml

Run it on every pull request that touches the specification, and a removed field or tightened limit fails the build before review. For GraphQL, schema diff tools do the same job against the schema.

Rerun the consumer’s view. A diff only sees the contract. The analyst’s secret weapon is the collection built months ago: unchanged, run it against the new version.

bru run payments-regression --env sit-v3-3 --reporter-html reports/compat-v3-3.html

Every failure is either an intended breaking change that needs a version and a migration, or an unintended one that needs a fix. Nothing in that collection was written for the new version, which is exactly why it represents existing consumers. The same principle drives regression testing in payments, and consumer-owned expectations are formalized in contract testing. Wiring both checks into a pipeline is covered in API tests in CI.

Review what tools cannot see. Changes to business rules, defaults, rounding, sort order, rate limits, and timing need a human reading the change list with the question “what would an existing consumer notice?”

How do you assess the impact of a breaking change on consumers?

Take one change and assess it consumer by consumer. Here is a realistic example.

The change: a partner-facing payments API, live long before the downstream limits were enforced, tightens creditor.name from maxLength: 140 to maxLength: 70, and remittanceInformation stays at 140, to align with the downstream scheme’s limits and stop silent truncation in the payment messages. The motivation is sound; the details of that downstream loss are in the ISO 20022 truncation ledger.

The assessment:

ConsumerUses field?EvidenceShare of requests over 70 chars (last 90 days)ImpactMigration effortOwner
Corporate portalYesGateway logs, UI limit is 1400.8%Rejected payments for long legal namesUI counter, validation message, abbreviation guidancePortal team
ERP connector (client A)YesLogs3.1%Batch rejections at month endClient changes mapping; needs notice and sandboxRelationship manager
Mobile appYesLogs, UI limit is 500%NoneNoneNone
Internal ops consoleYesCode searchUnknown, low volumeRejections on manual repairsAdd limit and counterOps tooling

The decision options:

  1. Ship as a breaking change in a new version, with deprecation of the old behavior and dates driven by client A’s release cycle.
  2. Ship in the current version but warn first: accept long names, return a warning, and log for 60 days before enforcing.
  3. Keep accepting 140 and apply an explicit, documented truncation rule, with the full name retained in a supplementary field.

The table is what turns a developer’s validation tweak into a business decision with owners. The share-of-requests column comes from logs or SQL, and getting it takes an hour with the skills in SQL for analysts; guessing it takes a production incident.

What goes in a good changelog entry?

## 2026-10-01 · v3.3.0

### Breaking (requires v3.3 opt-in)
- `creditor.name` maxLength reduced from 140 to 70.
  Requests over 70 characters return 400 FIELD_TOO_LONG.
  Why: aligns with scheme limits; prevents silent truncation.
  Migrate: enforce 70 in your UI or mapping. Sandbox available now.

### Added
- `PDNG` value in `PaymentStatus` (payment held for investigation, not final).
  Consumers must treat unknown statuses as non-final.

### Deprecated
- `legacyReference` response field. Use `endToEndId`.
  Deprecation: 2026-10-01 · Sunset: 2027-04-01

Every entry states what changed, what consumers will see, why, and what they must do. The Added enum value is flagged even though the contract calls it additive, because consumers who read nothing else read the changelog.

The APIs for Analysts learning path

Beginner: What is an API · API glossary · JSON for analysts · HTTP status codes · Your first collection · Why did my API request fail? · Reading an API contract

Intermediate: Analyze an API · Document an API · API test cases · Chaining and scripts · Webhooks · GraphQL

Advanced: POCs and demos · API design review · Versioning and breaking changes (you are here) · API security testing · API tests in CI

The takeaway

A breaking change is anything that makes an existing, correct consumer fail, including tightened validation, new response enum values, changed defaults, and behavior changes no schema shows. Ship breaking changes behind versions consumers opt into, as Stripe and GitHub do with date-based version headers. Deprecate with announcements, Deprecation and Sunset headers, usage data, a migration guide, and brownouts. Detect contract breaks with oasdiff, catch behavioral breaks by rerunning yesterday’s collection, and assess impact consumer by consumer with real request data before anyone sets a date.

For documenting changes consumers can act on, see API Documentation from Scratch, and for the regression and compatibility testing behind it, API Testing and QA Mastery for BAs. Facing a breaking change with real consumers attached? A 1:1 Tech BA Coaching Call will help you structure the impact assessment and the plan.

Ahmed is a Senior Technical Business Analyst with 10+ years in banking and payments. He builds practical guides and tools for analysts at The Tech BA Toolkit.

Tags: API Versioning, Breaking Changes, OpenAPI, Change Management, Systems Analysis

About the author

Analyst Engineering is written by Ahmed, a Senior Technical Business Analyst with 10+ years of banking and payments delivery experience: ISO 20022 and SWIFT messaging, payments API integration, Kafka event validation, and production support. Every article comes from real delivery work, and each one is reviewed and updated as tools and standards change.

Free account

Practice on the Labs, keep your progress

A free account, no password: an email link signs you in. It saves your steps and self-assessments on the Labs, shows your missions on a dashboard, unlocks the solutions, and, if you tick the box, sends you new missions and articles when they ship.

Your email is used to sign you in. Nothing else, unless you ask. Privacy.