Home/AI Backend Engineering/api-versioning-policy
A

api-versioning-policy

by @samberv
4.5(120)

Writes a complete API versioning and deprecation policy: choosing the version-carrying scheme (URI path, header, date-based account-pinned, or deliberate no-versioning), defining what counts as a breaking change, setting deprecation notice windows by audience, communicating sunset via RFC 9745 Deprecation and RFC 8594 Sunset headers, deciding fall-forward versus hard cutoff at the sunset date, and standing up breaking-change governance. REST version-and-sunset and GraphQL continuous schema evolution are treated as separate policies.

api-versioningdeprecation-policyapi-lifecyclebreaking-changessunset-headersGitHub
Installation
npx skills add https://github.com/samber/developer-platform-skills --skill api-versioning-policy
compare_arrows

Before / After Comparison

3 组
Before

Facing /v2, header versioning and date-pinned options, developers guess and argue in meetings without converging, so the policy document keeps slipping and no common rule exists before launch.

After

This Skill interviews along paradigm, audience, current state and change pressure, then ranks the options and hands back a publishable versioning and deprecation policy skeleton in one pass.

SKILL.md

API Versioning Policy

You are an API lifecycle strategist. A written versioning and deprecation policy covers:

  • Which scheme carries the version.
  • What counts as a breaking change.
  • How long consumers get before a version dies.
  • How you communicate and enforce that death.
  • Who signs off on breaking anything.

The hard half is not choosing a scheme - "choosing a versioning strategy is the easy half. The hard half is removing a version without breaking the consumers still on it."

This skill owns the policy: it decides what the scheme and its lifecycle rules are. Sibling samber/developer-platform-skills@public-api-design-review only checks at review time that a scheme exists and is applied consistently.

The wire-level contract is a different versioning surface from the client SDKs wrapping it. SDKs version under plain SemVer, and Stripe is the citable illustration of the split - a date-versioned, account-pinned API next to SemVer SDKs whose major-bump trigger is stricter than the API's version rule. Write the SDK policy separately; this skill covers the contract.

Memory (advised): When memory lives in a file, consider using developer-platform-context.md; if a different memory system is in use, rely on that instead. The file is an advisory reference, not a mandatory requirement. Separate task info in different sections. Remove finished tasks. Add a date to a task; no date for general project context. Some interview responses may differ between 2 tasks.

Interview

This is a strategy decision - interview before proposing anything. Ask one question per message, multiple-choice where offered, in this order. Question 1 comes first because its answer changes which later sections even apply.

  1. Paradigm: REST/HTTP, GraphQL, gRPC, or several surfaces? (REST defaults to version-and-sunset; GraphQL defaults to continuous schema evolution - see step 1.)
  2. Audience: internal only, partner/B2B under contract, fully public, or a mix? (This drives the notice period more than anything else - see step 4.)
  3. Current state: greenfield, one unversioned live API, or multiple versions already live? If versions exist: which scheme, and is any version both deprecated and still the default?
  4. Breaking-change pressure: how many breaking changes shipped or were blocked in the past year - none, one or two, roughly quarterly, or monthly-plus?
  5. Consumer reachability: can you identify and directly contact every consumer? Do any consumers ship mobile apps or run enterprise change-management cycles?
  6. Deadline: by what date must this policy - or the pending breaking change forcing it - land?
  7. One-off or standing: is this about retiring one version, or a policy the platform will operate for years?
  8. Effort ceiling: how much engineering time for versioning machinery, headcount for migration communication, and political capital for governance can you actually spend?

Questions 6-8 exist because the scheme options diverge sharply on time-to-effect, durability, and effort - the ranking in step 3 cannot be picked without them. A hard deadline promotes the low-machinery rungs; a years-long standing policy promotes the expensive ones.

If your harness has persistent memory, store the finished policy's core decisions (scheme, breaking-change definition, notice windows, enforcement style) so later deprecation runs start from the policy instead of re-deriving it.

Audience split: internal / partner / public

The split that drives every number in this policy is consumer reachability: the harder it is to identify and contact every consumer, the longer and more conservative the policy must be.

  • Internal consumers get coordinated deploys and contract tests instead of notice periods.
  • Partner consumers get negotiated SLAs.
  • Fully public consumers get 12-24 months, because mass communication is all you have.

This is why a hyperscaler and a mid-size platform land on the same ~12-month public floor while their internal APIs move in days.

Workflow

  1. Settle the paradigm - versioning vs continuous evolution.
  2. Write the breaking-change definition.
  3. Choose the version scheme.
  4. Set the notice windows and concurrent-version count.
  5. Design the sunset communication.
  6. Choose the enforcement style at the sunset date.
  7. Stand up governance proportionate to blast radius.
  8. Brainstorm candidate policies, then draft and validate the policy document.

1. Settle the paradigm

REST and GraphQL answer breaking change with different policies, not flavors of one:

  • REST/HTTP: version-and-sunset. Ship a new version consumers explicitly opt into, deprecate the old one, retire it on a schedule. Steps 3-6 apply in full.
  • GraphQL: continuous evolution. One continuously live schema; clients receive only the fields they request. Add a field, mark the old one @deprecated(reason: "..."), and remove it once telemetry shows zero real consumers. graphql.org states the position outright: GraphQL "takes a strong opinion on avoiding versioning by providing the tools for the continuous evolution of a GraphQL schema."
  • The @deprecated caveat. Apollo's caveat is the part teams miss: @deprecated "is a documentation and tooling signal, not a removal mechanism" - removal still needs usage monitoring and a written field-rollover strategy. For a GraphQL API, skip step 3's scheme menu and apply steps 4-7 field-by-field instead of version-by-version.
  • Neither is paradigm-locked. Shopify date-versions its GraphQL Admin API (YYYY-MM quarterly, same scheme as its REST surface) - a large production counter-example to "GraphQL means no versioning." Zalando's REST guidelines explicitly discourage versioning in favor of additive-only evolution. The paradigm sets the default policy; it does not dictate it.
  • gRPC: the breaking-change definition, notice windows, and governance (steps 2, 4, 7) apply unchanged; the scheme menu and HTTP-header communication mechanics in steps 3 and 5 are REST-specific. The gRPC-native equivalents - major version encoded in the proto package (AIP-185), buf breaking-change gates, proto deprecation options - are sibling samber/developer-platform-skills@public-grpc-api-design's territory; use it for the scheme and enforcement layer and keep this skill for the policy layer.

2. Write the breaking-change definition

A deprecation policy without a written breaking-change definition is unenforceable - every dispute becomes a judgment call. Write both lists into the policy:

  • Breaking (requires a new version): any change that makes a previously working client fail.
    • Removing or renaming a field or endpoint.
    • Changing a field's type.
    • Removing an enum value.
    • Adding a required request parameter.
    • Changing the status code or content type for the same scenario.
  • Non-breaking (ships into the current version): Stripe's published list is a citable starting taxonomy.
    • Adding new resources.
    • Adding new optional request parameters.
    • Adding new response properties.
    • Changing property order.
    • Changing the length or format of opaque strings (including ID prefixes).
    • Adding new webhook event types.
  • Open enums cut version churn: declare which enum fields may grow new values without a version bump, shifting tolerance onto callers instead of onto your compatibility machinery - Stripe documents this as an explicit backward-compatible carve-out.
  • A security escape hatch, scoped narrowly: LinkedIn's shape is the standard - even a strict minimum-window policy reserves the right to patch any version "for any critical security, privacy issues, or bug fixes." Write the exception down; never invoke an unwritten one.

Present the definition as a pragmatic simplification, not settled theory - the debate is real and your policy should survive a reader who has seen it.

  • Jeremy Ashkenas: "if your package has a minor change in behavior that will 'break' for 1% of your users, is that a breaking change?"
  • Nate Meyvis: "the very notion of a breaking change is relative to an API, and if the API doesn't exist [as a published contract], the notion is not well-defined" - which is precisely why the definition must be published.
  • Hyrum's Law: with enough users, every observable behavior is depended on regardless of contract. Declare what is explicitly not contractual (message text, ordering, timing).

The operational escape from the theory: watch breaking-change frequency. If you need a breaking change more than about twice a year, the scheme or the compatibility discipline is wrong - that is answerable from telemetry regardless of which side of the debate the team leans toward.

3. Choose the version scheme

Five candidate schemes for a REST surface. Ranked - say it out loud, don't leave it to row order:

  • effort: date-based account-pinned > no-versioning > header > URI == query

  • value: date-based account-pinned > no-versioning > URI > header > query

  • efficiency (value per unit of effort): URI > header > no-versioning > query > date-based account-pinned

  • Default rung: URI major version (/v1/users). Trivially visible, testable in a browser, cacheable per-version, demanding zero header sophistication from integrators - which is why it stays the working default for most public APIs despite Roy Fielding's objection (below). URI and query tie on effort because both are an afternoon of routing; query ranks last on value because it pollutes the query string and integrators rarely expect it there.

  • Move up to header versioning (a version or date in a request header, GitHub/LinkedIn-style YYYY-MM-DD) when URI stability matters to you or versions ship on a calendar cadence. Two opposite defaults exist within it and the policy must pick one: Stripe pins accounts to a version silently; LinkedIn refuses unversioned calls with an error. LinkedIn's choice is safer - a required version can never drift under a caller silently.

  • No-versioning (additive-only evolution) is a real rung, not an absence of policy: Zalando runs it for REST. Its cost is permanent - every change must be additive forever, and you need usage telemetry before removing anything. It fits reachable consumer bases with strong review discipline.

  • The starved option: date-based, account-pinned versioning (Stripe).

    • Highest value on the board: Stripe has "maintained compatibility with every version of our API since the company's inception in 2011."
    • Highest effort too: a gate-and-transformer chain where the engine computes only the latest shape, and per-version transformers downgrade each response, one module per historical breaking change, maintained forever.
    • It loses every efficiency round. Two conditions promote it anyway: breaking changes wanted many times a year, and a long tail of integrators you cannot force to migrate.
    • Weigh it against your actual surface size and change frequency; never adopt it reflexively because Stripe does it.
  • Deleted, not demoted: no-versioning, when the consumers are unreachable and breaking changes already ship quarterly or faster. Interview answers Q5 and Q4 together rule additive-only evolution out - it promises forever what the change rate is already breaking, against a base you cannot warn. Strike it from the menu rather than parking it last, because "we'll just be careful and stay additive" reappears as the plan every time the version-machinery estimate lands. Re-promotion trigger: breaking-change frequency back under roughly twice a year, or a consumer base you can enumerate and contact.

  • Fielding's objection, named because a colleague will raise it: the REST dissertation's author holds that the best practice for versioning is "don't" - an evolvable architecture shouldn't need a version number, and a version in the path changes the URL of a resource that hasn't changed. The no-versioning rung is the honest response to that argument; the URI default is the pragmatic one.

This ranking is a default, not a law. Re-rank against the interview:

  • A hard deadline (Q6) promotes URI.
  • A years-long standing policy with high change pressure (Q4, Q7) promotes date-based.
  • A team already running per-account configuration infrastructure gets Stripe-style pinning cheaper than the effort line assumes.

If Q1 answered GraphQL, this menu does not apply as a ladder at all - continuous evolution is the paradigm's own default policy, an argued opt-out of the ranking, not a hidden sixth rung. Shopify's date-versioned GraphQL API is the documented way back, if one unified versioning story across surfaces matters more.

Whatever wins: never allow unversioned access as a fallback path. Discord permitted it and developers named it directly as the anti-pattern - removing the unversioned path later is itself a breaking change nobody signed up for.

4. Set the notice windows and concurrent-version count

Set the numbers from audience (interview Q2/Q5), then check them against the published benchmarks in references/notice-period-benchmarks.md:

  • Fully public API: at least 12 months of deprecation notice - the floor Google Cloud and Shopify converge on.
  • Raise to at least 24 months when consumers ship mobile apps or are enterprises - Microsoft Graph and Meta's floor; app-store update cycles and enterprise change management are slow and outside your control.
  • Partner/B2B API: put the number in the contract or SLA, not on a public policy page - it is negotiable per partner and enforceable when written there.
  • Internal API: replace the notice period with consumer-driven contract testing and coordinated deploys. A notice period compensates for not being able to coordinate with consumers directly; internal teams can.
  • Concurrent versions: cap at 2-3 active (the cross-source consensus). Every additional live version multiplies maintenance, testing, and documentation cost. Shopify sustains ~4 by strict quarterly automation; Salesforce's dozens are the cautionary accumulation.
  • Define when the clock starts - version release, next version's release, or deprecation announcement. Meta's 2-year guarantee starts when the next version ships, a subtlety that moves real dates by months; ambiguity here becomes a support dispute later.
  • Publish dates as commitments, and treat published benchmarks as floors. Salesforce slipped a retirement two years; Twilio extended one EOL three times. Slipping is survivable but costly - it teaches integrators that "final" dates aren't final. Cite external benchmarks as "at least X months," never "exactly X."

5. Design the sunset communication

Deprecation is a staged process, not an event. Never skip from announce to removed.

  • Announce
  • Deprecate - both versions serving, signals live.
  • Sunset - date arrives.
  • Removal - grace period ends.
  1. **Ship the Deprecation header first

...

User Reviews (0)

Write a Review

Effect
Usability
Docs
Compatibility

No reviews yet

Statistics

Installs6.7K
Rating4.5 / 5.0
Version
Updated2026年10月23日
Comparisons3

User Rating

4.5(120)
5
37%
4
43%
3
13%
2
5%
1
2%

Rate this Skill

0.0

Compatible Platforms

🤖claude-code

Timeline

Created2026年10月4日
Last Updated2026年10月23日
🎁 Agent Knowledge Cards
Survey