api-versioning-policy
API のバージョニングと非推奨化ポリシーを策定する Skill です。URI パス・ヘッダー・日付ベースのアカウント固定・意図的な無バージョン化から方式を選び、破壊的変更の定義、対象別の非推奨通知期間、RFC 9745 Deprecation と RFC 8594 Sunset ヘッダーによる告知、Sunset 日での強制方法(段階移行かハードカットか)、破壊的変更のガバナンスを文書化します。REST のバージョン&サンセットと GraphQL の継続的スキーマ進化は別ポリシーとして扱います。
npx skills add https://github.com/samber/developer-platform-skills --skill api-versioning-policyBefore / After 効果比較
3 组/v2、ヘッダーバージョン、日付固定版などの選択肢を前に、開発者は勘で決めて会議を繰り返し収束せず、ポリシー文書は後回しになり、公開前に共通ルールが存在しない。
この Skill はパラダイム・対象・現状・変更圧力の順に問いを立て、方式のランキングとポリシー骨子を提示するため、一度のヒアリングで公開可能なバージョニング&非推奨ポリシーが完成します。
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.
- Paradigm: REST/HTTP, GraphQL, gRPC, or several surfaces? (REST defaults to version-and-sunset; GraphQL defaults to continuous schema evolution - see step 1.)
- Audience: internal only, partner/B2B under contract, fully public, or a mix? (This drives the notice period more than anything else - see step 4.)
- 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?
- Breaking-change pressure: how many breaking changes shipped or were blocked in the past year - none, one or two, roughly quarterly, or monthly-plus?
- Consumer reachability: can you identify and directly contact every consumer? Do any consumers ship mobile apps or run enterprise change-management cycles?
- Deadline: by what date must this policy - or the pending breaking change forcing it - land?
- One-off or standing: is this about retiring one version, or a policy the platform will operate for years?
- 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
- Settle the paradigm - versioning vs continuous evolution.
- Write the breaking-change definition.
- Choose the version scheme.
- Set the notice windows and concurrent-version count.
- Design the sunset communication.
- Choose the enforcement style at the sunset date.
- Stand up governance proportionate to blast radius.
- 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
@deprecatedcaveat. 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-MMquarterly, 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.
- **Ship the
Deprecationheader first
...
ユーザーレビュー (0)
レビューを書く
レビューなし
統計データ
ユーザー評価
この Skill を評価