The API versioning strategy decision record: why the versioning approach you chose in year one constrains the migration path for every breaking change in year three

API versioning looks like a URL prefix until a mobile client running version 1.2 refuses to update and your paying enterprise customer's integration breaks when you remove a deprecated field. The versioning mechanism you chose — URL path, header, date-based, or no explicit versioning at all — determines how many simultaneous API versions you can maintain without losing your mind, what migration friction your customers experience on every breaking change, and how long deprecated endpoints sit in your codebase consuming test coverage, documentation, and on-call attention years after you would have preferred to remove them. Most teams discover all of this not during the initial design session, but when the first unavoidable breaking change arrives and the path forward is shaped entirely by a decision that was made in ten minutes two years earlier.

A B2B SaaS company has been running its API without explicit versioning for eighteen months. The product works well. The API surface is small and the team knows the clients: a handful of integrations built by the customers themselves, using the same endpoints the product's own frontend calls. In February, the team decides to rename the user.full_name field to user.display_name for consistency with the new design system. The change ships on a Thursday afternoon. A note goes in the changelog: "API: renamed full_name to display_name for consistency." No breaking change warning. No deprecation period. No version bump, because there is no versioning system.

Three weeks later, the enterprise customer's integration team contacts support: their data pipeline has been throwing null reference exceptions, and the customer success dashboard — a tool built by the customer's data team that pulls user names from the API to populate a display field — has been silently failing for nineteen days. Every API call returned a valid 200 response with a valid JSON body; the full_name field was simply absent. The pipeline's null-check was on the overall response, not on the individual field. No exception was raised, no alert fired, and the dashboard continued to render stale cached values until someone noticed that the names had stopped updating.

The customer's data team spent two days tracing the failure to the renamed field. The SaaS team spent a day adding backward-compatible shims — returning both full_name and display_name in responses for the next 90 days to give all clients time to migrate. The customer accepted the explanation. But the incident opened a question the team could not answer cleanly: what else might have changed without notice? What is the process for distinguishing breaking changes from non-breaking additions? What notice period do customers receive before a breaking change ships? None of these questions had answers. The decisions that determined the API's stability contract were never written down.

Why API versioning is an architectural decision, not a URL convention

The choice of a versioning strategy is often framed as a preference question — "do you prefer /v1/ in the URL or a version header?" — and decided accordingly: whoever speaks first in the design meeting, or whichever approach the lead engineer used at a previous company, becomes the answer. This framing misses what the versioning strategy actually determines.

The versioning strategy determines the client migration model. URL path versioning — /api/v1/users, /api/v2/users — requires clients to change every URL that references a versioned endpoint when migrating from v1 to v2. A client that has hardcoded 20 API endpoints must update 20 URLs. Header-based versioning — X-API-Version: 2 or Accept: application/vnd.api.v2+json — requires the client to change a single configuration value; all URLs remain the same. Date-based versioning — where the version is a date and the server applies the behavior that was current as of that date — allows clients to opt into new behavior incrementally, endpoint by endpoint, by updating their version date at their own pace. Each model produces different migration friction, different error modes when the wrong version is specified, and different rollback complexity when the migration is partially complete.

The versioning strategy determines the codebase maintenance model. When two API versions coexist, the codebase contains the business logic for both. In URL path versioning, the two versions often live in separate controller files or separate route namespaces — app/controllers/api/v1/ and app/controllers/api/v2/. In header-based versioning, the dispatch logic lives in a single controller with conditional branching on the version header. In date-based versioning (as used by Stripe), a centralized version adapter layer applies transformation diffs to request inputs and response outputs, with the application logic operating on a single internal representation. Each model produces a different structure for where version-specific code lives, how many code paths must be tested, and how a new breaking change in v3 affects the existing v1 and v2 code.

The versioning strategy determines the sunset feasibility. A deprecated API version can only be removed when no clients are using it. The versioning strategy determines how easily the team can observe which clients are using which version (URL path versioning makes version visible in access logs trivially; header-based versioning requires request header logging to be configured explicitly; no versioning means there is no version signal to observe) and how easily clients can be contacted and motivated to migrate. Enterprise customers with annual contracts may have contractual rights to a specific API behavior — the versioning strategy determines whether those contracts can be honored by maintaining the version indefinitely or whether the team is negotiating exceptions to a policy that was never formally documented.

The versioning strategy determines the authentication and authorization surface across versions. When an API evolves from v1 to v2 with changes to the permission model — adding resource-level scopes, introducing tenant isolation, removing a legacy admin bypass — the versioning strategy determines whether the new security model applies to all API traffic or only to v2 traffic. A client that continues to use the v1 endpoint after v2 is launched may be operating under the old permission model indefinitely if the team does not explicitly apply the new security model to v1 as well. This is a common source of security regressions: the new authorization logic is written for the new version, tested against the new version, and the old version continues to operate with the old — potentially weaker — permission check.

Versioning strategies: the structural properties of each approach

Four API versioning strategies are in common production use. Each has structural properties that are independent of implementation quality — advantages and limitations that are inherent to the approach, not to the specific implementation.

URL path versioning encodes the version in the URL path: /api/v1/users, /api/v2/users. This is the most common approach in production APIs and the approach with the best tooling support across every layer of the infrastructure stack. The structural advantages are: the version is visible in every request log, every error report, every network trace, every analytics event, and every link shared between engineers — no header inspection required to determine which version a request is using. CDNs and reverse proxies cache version-1 and version-2 responses independently without any Vary header configuration. API documentation tools (Swagger, Redoc, Postman) support multiple URL-versioned APIs natively. Client library generators that produce v1 and v2 client stubs can be run independently with minimal shared configuration. The structural limitations are: every URL that a client stores, hardcodes, or uses in a bookmark must be updated on version migration, and the URL namespace grows with each version — a legacy v1 endpoint sitting alongside v3 for backward compatibility produces URL structures that require explanation in the documentation. URL path versioning also encourages treating the API version as a global container: if v2 changes one endpoint's response shape, clients that do not need that endpoint are still encouraged to migrate to v2 to "be on the latest version," even though their integration with the unchanged endpoints works identically under v1 and v2.

Header-based versioning accepts the version in a request header: Accept: application/vnd.whychose.v2+json (the media type approach, per REST purists) or X-API-Version: 2 (a custom header, pragmatically common). The structural advantages are: URLs are stable across versions — a client that migrates from v1 to v2 changes a single configuration value, and every URL in the client's code remains the same. Multiple API versions can be tested in parallel by changing a single header in the request, without switching URL base paths. The structural limitations are: the version is invisible in access logs unless header logging is explicitly configured — the access log shows GET /api/users 200 for both v1 and v2 requests, and determining which version was called requires correlating the request with the application-layer log that recorded the header. CDN caching requires Vary: Accept or Vary: X-API-Version configuration, which many CDN setups do not handle correctly — a CDN node that caches a v1 response and serves it to a v2 request produces incorrect behavior with no error. Browser clients that make cross-origin requests must include the version header in the CORS preflight Access-Control-Allow-Headers response, which is a recurring configuration source of browser-based integrations that fail silently. The media type approach (application/vnd.whychose.v2+json) adds the version to the HTTP protocol-correct location, but is unfamiliar to most client developers and produces ugly header values that are difficult to remember and easy to mistype.

Date-based versioning, popularized by Stripe, encodes a date in a header — Stripe-Version: 2024-06-01 — and the server applies the behavior that was current as of that date. When Stripe ships a breaking change on 2024-09-15, clients that specified Stripe-Version: 2024-06-01 continue to receive the pre-breaking-change behavior indefinitely; clients that upgrade to Stripe-Version: 2024-09-15 receive the new behavior. The date-based model allows clients to opt into new breaking changes at their own pace, endpoint by endpoint, without a global version migration. The structural advantages are: there is no "v1 vs v2" cliff where a client must migrate everything at once; the deprecation window is a date range rather than a version retirement; new engineers at the company can read the version history as a timeline of when specific behaviors changed. The structural limitations are: the implementation complexity is significantly higher than URL path or header versioning — the server must maintain a version transformation layer (Stripe calls it "version middleware") that knows exactly when each behavioral change was introduced and how to transform requests and responses to match the requested date's behavior. Testing requires covering the full matrix of version dates × endpoint behaviors. Clients that specify a very old version date accumulate all the transformations between their version and the current implementation, which compounds complexity over time. Date-based versioning is the right choice for a high-traffic API with hundreds of enterprise integrations that require maximum flexibility — it is disproportionate overhead for an early-stage product serving tens of integrations.

Eternal backward compatibility — no explicit versioning, with a commitment to never make breaking changes — is a valid strategy for specific API surface areas and a catastrophic one for others. GraphQL APIs often adopt this model: because GraphQL clients specify exactly which fields they need in each query, the server can add new fields without affecting existing clients, and removing fields is a breaking change that is detected immediately by any client that requested the removed field. REST APIs that adopt eternal backward compatibility must maintain every field name, every response shape, and every behavior indefinitely — which is sustainable for a small, stable API surface but produces accumulating complexity as the domain model evolves and the API surface diverges from the internal representation. The field aliasing required to keep full_name returning the same value as display_name after the rename is a minor example; the pattern scales badly when the internal model undergoes substantive changes (adding multi-tenancy, splitting a resource into two resources, changing the type system for money fields). The database migration strategy intersects here: the backward-compatibility layer that keeps old API fields working after internal model changes requires either database-level aliasing or application-level transformation, and both approaches produce technical debt that the team carries indefinitely.

What constitutes a breaking change — and the common misclassifications

The definition of "breaking change" is rarely documented explicitly, which produces two failure modes: engineers who ship breaking changes without realizing they are breaking — the full_name/display_name rename falls into this category — and engineers who treat non-breaking additions as breaking changes requiring version bumps, producing unnecessary version proliferation and client migration burden.

Breaking changes that are universally recognized: removing an endpoint; removing a field from a response; renaming a field in a response; changing the type of a field (string to integer, integer to string, object to array); removing a previously-accepted request parameter; making a previously-optional parameter required; changing the HTTP status code returned for a specific condition; changing the authentication scheme required for an endpoint; removing a previously-granted OAuth scope; changing the meaning of a status code or error code in the error response body.

Breaking changes that are commonly misclassified as non-breaking: changing the sort order of a list response (clients that rely on the first element being the most recently created, or on a specific positional relationship between elements, break silently — no error is raised, the wrong element is processed); changing the granularity of a timestamp field from seconds to milliseconds (clients that parse the timestamp as an integer and compare it to a threshold may produce incorrect comparisons after the precision change); adding a new required field to a webhook payload that the client must acknowledge (clients that do not handle the new field produce incomplete acknowledgements); changing the format of an identifier from integer to UUID (clients that treat the ID as a number, sort by it, or use arithmetic operations on it break); changing an error response body structure (clients that parse the error.message field to display a user-facing error receive the wrong content or a null after the field is moved to error.details[0].message).

Changes that are non-breaking for well-behaved clients: adding a new optional field to a response (well-behaved clients ignore fields they do not recognize); adding a new optional parameter to a request (clients that do not send the parameter receive the previous default behavior); adding a new endpoint; adding a new HTTP method to an existing resource (a PATCH endpoint on a resource that previously only supported GET and POST); relaxing a validation constraint (a field that previously required a minimum length of 8 characters now accepts 6 characters — existing clients that produce 8+ character inputs are unaffected); adding a new value to an enum field in a response (clients that switch on the enum value and have a default case handle the new value correctly; clients without a default case fail — this is the "additive breaking change" category that depends on client implementation quality, not on the change itself).

The breaking change classification process must be explicit in the ADR because the classification determines the deployment decision: does this change require a version bump, a deprecation period, and a migration guide, or can it ship as a regular deployment? Without an explicit classification, individual engineers make the call in the pull request review, applying their own mental model of what "breaking" means. The accumulation of individual judgment calls produces an inconsistent track record — some changes that were genuinely breaking shipped without a version bump, some harmless additions triggered unnecessary deprecation processes — and an enterprise customer who has been burned once by an unannounced breaking change has no way to determine which future changes will follow the version-bump process.

Deprecation lifecycle: the mechanics of retiring an API version

A versioning strategy that cannot retire old versions is not a versioning strategy — it is a permanent promise to maintain every API version ever shipped. The deprecation lifecycle defines how an old version transitions from "supported" to "deprecated" to "sunset" and what signals clients receive at each stage.

The Deprecation and Sunset headers (per RFC 8594 and RFC 8607) are the standardized mechanism for communicating deprecation timeline to clients at the HTTP layer. Deprecation: true or Deprecation: Tue, 01 Sep 2026 00:00:00 GMT signals that the response is from a deprecated API version. Sunset: Mon, 01 Dec 2026 00:00:00 GMT signals the date after which the endpoint will no longer be available. Clients that consume these headers — including monitoring agents that alert on the presence of Deprecation headers — receive the signal without requiring the client developer to monitor a changelog. The headers are low-friction to implement: a middleware that checks the requested API version against the deprecation registry and adds the headers if the version is in the deprecated range. Most client developers do not currently parse these headers, but the monitoring tools that enterprise integrators build often do — the header is a machine-readable contract, not just a documentation convention.

The minimum deprecation notice period must be a specific number of calendar days, not a qualitative policy like "reasonable notice" or "best effort." What is reasonable to the engineering team at a startup — two weeks — is categorically unreasonable to an enterprise customer whose integration team operates on quarterly planning cycles and requires sign-off from a security review board before modifying any system that touches customer data. Ninety days is the minimum for a B2B SaaS API that serves enterprise customers. One hundred eighty days is more defensible if the product is used in regulated industries (healthcare, finance, government) where software change management processes operate on multi-month cycles. The ADR must specify the minimum deprecation notice period as a concrete number and explain the rationale — what integration development and testing cycle the notice period was designed to accommodate.

Traffic monitoring on deprecated endpoints is the only mechanism that determines when sunset is safe. A deprecated endpoint can be sunset when no clients are actively using it. "No clients" requires confirmation that traffic has dropped to zero or to a floor that represents monitoring noise (health checks, uptime monitors, load balancer probes). Monitoring traffic to deprecated endpoints by version requires the API version to be visible in the access log, which is trivial for URL path versioning (grep "/api/v1/" access.log | wc -l) and requires configured header logging for header-based versioning. The sunset process must include a traffic review gate — "confirm that traffic to deprecated version endpoints has been below N requests per day for M consecutive days" — before the removal PR is merged. Without this gate, the team removes the endpoint and discovers three days later that a rarely-used integration (the quarterly export job run by the CFO, the annual audit report generator, the customer's overnight batch reconciliation job) has broken.

The maximum concurrent version count is the other side of the sunset policy: the team commits to never maintaining more than N simultaneous API versions. If the maximum is two, the release of v3 triggers the deprecation of v1 regardless of client migration status. This commitment bounds the maintenance overhead — a team maintaining three simultaneous API versions is running three test suites for overlapping functionality, three sets of documentation, and three code paths for every security fix. The tradeoff between client migration flexibility (high maximum = clients can defer migration indefinitely) and engineering overhead (low maximum = forced migration schedule) must be made explicitly and documented. The common failure mode is to have no maximum, which produces a team that is indefinitely maintaining v1 three years after v4 was released because one enterprise customer signed a contract that referenced v1 API stability and the legal team will not authorize a forced migration.

Mobile API versioning and the long-tail client problem

Web clients — browsers and server-side integrations — can be updated to use a new API version in hours or days: deploy the updated code, and all clients immediately use the new version. Mobile clients cannot be updated unilaterally: a user who has not installed the new version of the iOS or Android app continues to run the old version, calling the old API endpoints. App store review processes add days to the deployment timeline. Users who do not auto-update their apps may continue to run versions that are months or years old. The error handling strategy must account for the case where the API returns an error code indicating that the client version is too old — and the client must surface a meaningful message, not an opaque 400.

The forced-upgrade mechanism is the primary lever for retiring old mobile app versions. When the API receives a request from a mobile client that is running a version below the minimum supported version, the API can either: return a 410 Gone response with a structured error body indicating the minimum version and a link to the app store (hard cutoff, enforced by the API); return a successful response with an additional response header (X-Minimum-App-Version: 3.2.0) that the mobile client reads and uses to display an upgrade nag (soft warning, enforced by the client); or combine both — a warning period where the header is present but the response succeeds, followed by a hard cutoff date after which the 410 is returned. The forced-upgrade mechanism must be implemented and tested before the first API version is deprecated, not discovered under time pressure when the deprecation date arrives and old mobile clients are still making up 15% of production traffic.

The mobile version distribution — what percentage of active mobile clients are running each app version — must be monitored as part of the deprecation lifecycle. Most mobile analytics SDKs report app version in their telemetry. For a product that does not use a third-party analytics SDK (per the observability strategy ADR), the API access log with a User-Agent header that includes the app version provides the distribution. A deprecation timeline that schedules sunset for a date when 30% of mobile clients are still running the deprecated version is a timeline that will not be met: the team either extends the deprecation, introduces the forced-upgrade with short notice, or sunsets the endpoint and generates a wave of support tickets from users who cannot use the product. The distribution must reach a threshold — typically below 1% on the deprecated app version — before sunset is safe.

The mobile API version policy must specify how many major app versions are supported simultaneously. If the current app is version 4.x and the team commits to supporting clients back to version 3.x, the API must maintain the API surface used by version 3.x for the life of that support commitment. If version 3.x used the v1 API and version 4.x uses the v2 API, the v1 API cannot be sunset until the version 3.x support commitment expires or until the version 3.x user base drops below the sunset threshold. This policy must be decided explicitly and documented in the ADR, because it directly determines the minimum API deprecation notice period in practice — the notice period is bounded by the minimum time for users to upgrade from the oldest supported mobile app version to the current version, not by the engineering team's preference for a 90-day window.

Enterprise customer migration: the contractual dimension

Enterprise customers who integrate with a B2B SaaS API often expect implicit or explicit contractual commitments about API stability. The versioning strategy ADR must address the enterprise dimension because enterprise migrations operate on fundamentally different timescales than the timelines engineering teams design for.

Enterprise integration timelines are driven by procurement cycles, change management processes, and organizational risk tolerance. A breaking API change that requires the customer to update their integration may require: a security review of the modified integration (2–4 weeks for a standard enterprise security review process, 8–12 weeks for a SOC 2 or FedRAMP environment); a quality assurance and regression testing cycle (2–3 weeks); a change advisory board approval (weekly or monthly meeting cadence, with a 2–6 week lead time for agenda placement); a production deployment window (often monthly, sometimes quarterly for regulated environments). A 90-day deprecation notice that the engineering team considers generous may cover a security review but not the full change management process for an enterprise customer in a regulated industry. The ADR must specify the deprecation notice period with the enterprise migration cycle in mind, not with the startup deployment cycle in mind.

API version commitments in customer contracts are a legal constraint that the versioning strategy ADR must acknowledge. If the customer's contract includes language that guarantees the API will not make breaking changes without 180 days' notice, or that version X of the API will be available for the duration of the contract term, the engineering team cannot unilaterally sunset that version without a contract amendment. These commitments are often made during the sales process by account executives who do not consult the engineering team about implementation feasibility. The ADR must specify the maximum API stability commitment that sales is authorized to make (and the process for escalating non-standard commitments for engineering review before they are included in a contract). Without this constraint, the versioning strategy is the plan the engineering team made and the contract language is the actual binding commitment — and they may conflict, with the contract winning.

A dedicated sandbox environment for migration testing is a frequently overlooked component of enterprise API migrations. An enterprise customer that needs to validate their integration against the new API version before migrating their production integration needs access to a stable test environment that accurately represents the new API version's behavior. If the staging environment is shared with active development, it may not be stable enough for a customer to complete a multi-week regression test. If there is no staging environment at all, the customer must validate against production with a test account — which complicates the test because production data and production traffic may interfere. The ADR must specify whether a stable sandbox environment is provided for migration testing and what the process is for customers to request access.

API versioning decisions in AI chat history

API versioning produces a specific pattern in AI chat history: the initial design decision appears in early-stage conversations and is often not revisited until the first breaking change is required, at which point multiple sessions address the mechanics of the migration without ever returning to the original versioning rationale.

The initial versioning decision sessions are typically brief and informal. The conversation pattern: "we're building a REST API — should we version it with /v1/ in the URL or use a version header?", followed by a discussion that covers the surface-level tradeoffs (URL versioning is more common; headers are cleaner; Stripe uses dates) without reaching a conclusion about which approach fits the specific product's client profile, deployment model, or enterprise commitment posture. The decision is made — often "let's just do /v1/ in the URL, it's the simplest" — and the conversation ends without documenting the rejection rationale for headers, the maximum version commitment the team is willing to maintain, or the breaking change classification policy. Three years later, the engineer who needs to sunset v1 does not know whether the initial session considered the enterprise migration timeline at all. Three months of AI chat history typically contain this conversation and nothing else on the topic — a one-time decision that was made informally and never revisited explicitly.

The first breaking change sessions are the highest-value recovery target for API versioning decisions in AI chat history. The conversation pattern: "we need to rename a field in our API response — how do we handle backward compatibility?", "we're changing our user model and the old API shape doesn't make sense anymore — do we need to make a v2?", "how do we communicate to customers that we're deprecating an endpoint?". These sessions contain the team's first concrete encounter with the consequences of the original versioning decision: how many simultaneous versions the codebase can support, whether the current deprecation headers implementation covers the enterprise customers' monitoring tooling, how long the minimum notice period should be given the specific customer contracts in place. The reasoning in these sessions is often more nuanced than the initial decision session — the first breaking change forces concrete thinking about the migration model that the initial design session abstracted away. The conclusions reached in these sessions (a 90-day deprecation window was established, the Deprecation and Sunset headers were added to the middleware) are exactly the decisions that should update the API versioning ADR, but rarely do — they remain in a closed chat session, invisible to the next engineer who needs to ship a breaking change.

Version traffic monitoring sessions capture the operational mechanics discovered during deprecation: "how do I see which API version our customers are using?", "how do I check if anyone is still calling the v1 endpoints before I turn them off?", "we have one customer still calling /api/v1/users — how do we find out who it is?". These sessions contain the actual monitoring approach that was implemented — whether version traffic was added to the access log as a field or inferred from the URL path, whether the monitoring query is a log aggregation or a database query against request metadata, whether there is a per-client version report or only an aggregate. The operational approach documented in these sessions is part of the versioning strategy ADR's sunset procedure — the "confirm that traffic to the deprecated version has dropped to zero" step requires knowing specifically how to measure that traffic. Without the AI chat sessions that established the monitoring approach, the engineer tasked with the sunset can only guess how to verify the traffic gate, and may make the wrong guess. The open-source extractor surfaces these conversations from the AI chat history, where the monitoring rationale is preserved in full but has never been consolidated into the sunset procedure documentation.

Enterprise customer escalation sessions capture the decisions made when a specific customer's migration timeline conflicts with the planned sunset date: "an enterprise customer says they can't migrate from v1 to v2 by our deadline — what are our options?", "can we extend the v1 sunset for one customer without extending it for everyone?", "what do we do if the enterprise customer's contract says we need to give 6 months notice but our deprecation policy says 90 days?". These sessions contain the actual policy decisions that were made under deadline pressure: whether individual sunset extensions were granted and on what conditions, whether the contract language was treated as binding or as aspirational, whether the deprecation window was extended globally or a private fork of the deprecated API was maintained for the specific customer. These decisions, made under the pressure of a real customer deadline, are exactly the decisions that should inform the API versioning ADR's enterprise migration policy section — but they are captured only in AI chat sessions that were never linked to the versioning strategy documentation.

Writing the API versioning strategy ADR

The API versioning strategy ADR needs five sections. Each section answers questions that different stakeholders will ask at different stages of the API lifecycle — from the engineer implementing the first breaking change to the account executive negotiating a new contract to the on-call engineer monitoring traffic on a deprecated endpoint the night before its scheduled sunset.

Section 1: Versioning mechanism. The versioning approach chosen: URL path versioning, header-based versioning, date-based versioning, or permanent backward compatibility. The specific implementation: the URL prefix pattern (/api/v{N}/), the header name and value format, or the date format if date-based. The router-level dispatch mechanism that reads the version identifier and routes to the correct handler — middleware, router configuration, or controller-level branching. The default version behavior when no version identifier is provided — either a redirect to the current version, a 400 error requiring explicit versioning, or a default to the oldest maintained version. The evaluation of the alternative versioning approaches that were considered, with rejection rationale specific to this product's client profile: why URL path versioning was preferred over header-based (or vice versa), what made date-based versioning disproportionate overhead for the current stage, whether the decision should be revisited when the customer count or enterprise ratio changes. The versioning mechanism must also cover internal API clients — whether the same versioning strategy applies to service-to-service calls within the product's own infrastructure or whether internal clients use a separate internal API that is exempt from the external versioning policy.

Section 2: Breaking change definition and classification process. The exhaustive list of change types that require a version bump, drawn from the canonical breaking changes (field removal, field rename, type change, endpoint removal, parameter enforcement change, status code meaning change, authentication scheme change) and from the product-specific changes that have been classified as breaking through incident experience (sort order changes, timestamp precision changes, identifier type changes, error body structure changes). The list of change types that are explicitly non-breaking additions (new optional fields, new optional parameters, new endpoints, constraint relaxation). The classification process: who reviews new changes against the breaking change list, at what stage in the development process (design review, pull request review, or pre-deployment check), and what the escalation path is when the classification is ambiguous. The change log requirement: every breaking change must include a migration guide that specifies the old behavior, the new behavior, and the exact code changes required in each client SDK (if SDKs are maintained). The security review process must be integrated into the breaking change classification for changes that affect authentication, authorization, or access control — a breaking change to the permission model requires security review before the migration guide is published.

Section 3: Deprecation lifecycle. The minimum deprecation notice period in calendar days, with the rationale — what customer integration cycle the period was designed to accommodate. The deprecation communication channels: Deprecation and Sunset HTTP headers (implementation specification — the middleware component, the version deprecation registry it consults, the date format used in the Sunset header); developer changelog entries (format, distribution mechanism — email to registered developers, in-app notification, changelog RSS feed); direct outreach to customers who are actively using the deprecated version (how are active users of the deprecated version identified, who sends the outreach, what the message says). The maximum concurrent version count: the number of simultaneous API versions the team commits to maintaining at any point, and the consequence when a new version is released that would exceed the maximum — which existing version moves to deprecated status. The sunset traffic gate: the specific threshold below which traffic to a deprecated version is considered safely sunset-able (absolute request count per day, percentage of total API traffic, or both), the monitoring query or dashboard that provides the measurement, and the process for confirming the gate condition before the sunset PR is merged. The sunset execution procedure: the order in which removal steps occur (deprecation header updated to reflect expired status, routing updated to return 410, endpoint code removed, database schema changes enabled that the deprecated endpoint was blocking).

Section 4: Mobile client policy. The minimum supported mobile app version, expressed as a version number and a rationale for the cutoff (the version that introduced the last incompatible API dependency). The forced-upgrade mechanism: what HTTP status code and response body are returned to mobile clients below the minimum version, how the mobile app interprets the response and presents it to the user (error screen, upgrade modal, redirect to app store), and the server-side configuration for the minimum version threshold. The mobile version distribution monitoring: where the app version distribution is reported (access log User-Agent field, in-app analytics event, or both), the query or dashboard for reading the distribution, and the distribution threshold that must be reached before a deprecated API version can be sunset when mobile clients are among its users. The mobile support window: how long a major mobile app version is supported after a new major version is released (the team commits to maintaining API compatibility for N months after the previous major version's release), and how this support window interacts with the API deprecation notice period.

Section 5: Enterprise customer migration policy. The minimum deprecation notice commitment that sales is authorized to make in customer contracts without engineering review — the floor, below which sales must escalate for engineering approval. The process for escalating non-standard deprecation notice commitments: who approves, what information must be provided (the specific customer's integration complexity, the regulated environment constraints, the contractual consequences of a missed sunset), and the SLA for an approval decision. The sandbox environment policy: whether a stable sandbox that matches the current deprecated and new API versions is available for customer migration testing, the process for requesting access, and the stability commitment for the sandbox (whether it receives production deployments or is frozen at a specific version for the duration of the customer's migration testing). The enterprise migration support process: whether a dedicated migration support engineer is available to assist enterprise customers during the migration window, what that support includes (code review of updated integration, migration test plan review, joint testing session), and which customers qualify based on contract tier or revenue threshold. The sunset exception process: the conditions under which an enterprise customer can receive a private sunset extension beyond the published deprecation window, what the maximum extension duration is, what the customer must provide in exchange (a binding commitment date for migration completion, an escalation to their technical team), and whether the extension is publicly acknowledged in the changelog or treated as a private arrangement. The data retention policy intersects here: if an API version that is under a sunset extension provides access to data that the retention policy would otherwise require to be purged, the conflict must be resolved explicitly — either the data retention policy creates an exception for the duration of the extension, or the sunset extension is not available for endpoints that expose data subject to retention constraints.

API versioning is the decision that determines what every future breaking change costs. A versioning strategy chosen in ten minutes in year one — "let's just do /v1/ in the URL" — is not wrong by itself. It becomes wrong when it is applied to a year-three product with enterprise customers, contractual stability commitments, and mobile clients running versions two years old, without any documentation of what the migration model is, what constitutes a breaking change, or how long a deprecated version must be maintained before sunset. The decisions that determine API evolution are made early, when the cost of the wrong choice is invisible, and discovered late, when the cost of reverting is prohibitive. The ADR preserves the rationale for the version-one decision alongside the classification policy, the deprecation lifecycle, and the enterprise migration commitments that were established as the API grew — the decisions that shaped the versioning strategy in practice rather than the version-one decision that only established the mechanism. The open-source extractor recovers the AI chat sessions where these later decisions were made, from the conversations where a concrete customer escalation produced a concrete policy, before that policy was archived in a closed chat tab and forgotten.