AP

api-versioning-strategy

Provides patterns for safe API versioning, including date-based pinning, Deprecation/Sunset headers, and version transformers.

Install

mkdir -p .claude/skills/api-versioning-strategy-curiositech && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/11748" && unzip -o skill.zip -d .claude/skills/api-versioning-strategy-curiositech && rm skill.zip

Installs to .claude/skills/api-versioning-strategy-curiositech

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Choosing and operating an HTTP API versioning strategy that doesn't break clients — Stripe's date-based pinned versions, the Deprecation/Sunset header pair (RFC 9745 + RFC 8594), URI vs header vs media-type approaches, and the version-transformer pattern. Grounded in Stripe's published architecture and IETF RFCs.
314 charsno explicit “when” triggerlonger than Claude Code's old 250-char listing cap (fine on current versions)
Advanced

Key capabilities

  • Choose an HTTP API versioning strategy
  • Implement date-based pinned versions (Stripe's model)
  • Apply Deprecation and Sunset headers for lifecycle management
  • Compare URI, header, and media-type versioning approaches
  • Utilize the version-transformer pattern
  • Announce and sunset API changes on a date

How it works

The skill guides the selection of an API versioning strategy, focusing on date-based pinned versions for public APIs and additive evolution with Deprecation/Sunset headers for internal APIs. It compares different versioning approaches and explains the version-transformer pattern.

Inputs & outputs

You give it
API change requiring versioning or deprecation
You get back
recommended API versioning strategy and implementation guidance

When to use api-versioning-strategy

  • Implementing date-based versioning
  • Adding deprecation and sunset headers to endpoints
  • Transitioning from URI versioning to header-based approaches

About this skill

API Versioning Strategy

TL;DR: Date-based versions pinned per API key (Stripe's model) beat /v1/, /v2/ for long-lived public APIs because they let you make small breaking changes without forcing clients onto a new tree. For internal APIs, additive evolution + the Deprecation and Sunset headers (RFC 9745, RFC 8594) is usually enough. Always announce, always sunset on a date, never just remove.


Jump to your fire

SymptomSection
"Need to break a field but have 100k API keys"Date-pinned versions
"Should I do /v1/ vs Accept: application/vnd.foo.v2+json?"Strategy comparison
"How do I tell clients an endpoint is going away?"Deprecation + Sunset
"Maintaining 6 versions in code is killing us"Version transformers
"Internal API — do we even need versioning?"Internal vs public

Decision diagram

flowchart TD
  A[Need to change API behavior] --> B{Additive only?<br/>new field, new endpoint, new optional param}
  B -->|Yes| C[Just ship it<br/>no versioning needed]
  B -->|No, breaking| D{API is public<br/>+ many opaque clients?}
  D -->|No, internal/few clients| E[Coordinate migration<br/>+ Deprecation/Sunset headers]
  D -->|Yes, public| F{Can you rev compat layer<br/>per request?}
  F -->|Yes, have a transformer| G[Date-based pinned version<br/>Stripe model]
  F -->|No, big rewrite| H[Major-version URI bump<br/>/v1 → /v2]
  E --> I[Set Deprecation: @timestamp<br/>+ Sunset: HTTP-date<br/>+ Link: rel=deprecation]
  G --> I
  H --> I

1. Date-based pinned versions (Stripe's model)

Stripe published its architecture in API versioning at Stripe:

The first time a user makes an API request, their account is automatically pinned to the most recent version available, and from then on, every API call they make is assigned that version implicitly.

[Versions are] rolling versions that are named with the date they're released (for example, 2017-05-24).

The key properties:

PropertyWhy it works
Per-account default version (set on first call)New customers automatically pinned to latest; existing customers don't break when you ship a change
Stripe-Version header overrides the pin per-requestAllows gradual client migration: SDK can opt-in to a new version before the account does
Date-based names (2024-04-10)Conveys recency; no debate about what "v3" means; allows arbitrarily many small breaks instead of saving them up for a big-bang v3
Dashboard upgrade pathCustomer can preview the diff, then upgrade their account-default version

This is the only approach that scales to truly long-lived public APIs (Stripe has versions going back a decade). It costs you internal complexity (the transformer pattern in §4) but spares your customers the perpetual /v1//v2/ migration cycle.


2. Versioning strategy comparison

StrategyWhere the version livesProConBest for
URI segment (/v1/, /v2/)PathCache-friendly (different URL = different cache entry); zero client configCan't make small breaks; forces a tree fork; URLs are no longer "stable resource identifiers" (per Fielding)Internal APIs, public APIs that rarely break
Custom header (Stripe-Version: 2024-04-10)Request headerTons of versions cheap; per-request granularityCan't share URLs with version baked in; harder to test in browser address barPublic APIs at scale
Accept media-type (Accept: application/vnd.foo.v2+json)Standard Accept header"Spec-correct" per HTTP; reuses content negotiation machineryAwkward to set; tooling support varies; debugging via curl is verboseHypermedia APIs, deeply RESTful designs
Query parameter (?version=2)URLEasy to test; visible in logsPollutes URL; semantically wrong (version isn't a resource property)Rapid prototyping only
No versioning, additive onlyn/aZero overhead; no client coordinationCan never make a breaking change without a new endpointInternal microservices with shared deploy

Pick by selecting your constraint:

  • "We make tiny breaks frequently" → date-based header (Stripe)
  • "We do a big rewrite every 5 years" → URI segment (/v1/, /v2/)
  • "We never break, only add" → no versioning + additive evolution

3. The Deprecation/Sunset header pair

Two IETF RFCs cover the lifecycle signal:

Sunset header — RFC 8594

The Sunset value is an HTTP-date timestamp, as defined in Section 7.1.1.1 of [RFC7231], and SHOULD be a timestamp in the future.

Sunset: Sat, 31 Dec 2026 23:59:59 GMT

Indicates "the resource is expected to become unresponsive at a specific point in the future." Clients SHOULD treat the timestamp as a hint, not a hard contract.

Deprecation header — RFC 9745

Deprecation: @1735689599
Sunset: Sun, 31 Dec 2026 23:59:59 GMT
Link: <https://api.example.com/docs/migrate-v1-to-v2>; rel="deprecation"

The Deprecation value is a Unix timestamp (seconds, prefixed with @ per Structured Fields). It can be in the past ("already deprecated") or future ("will be deprecated"). The MUSTs:

  • MUST use the structured-field date format per RFC 9651
  • Sunset MUST NOT be earlier than Deprecation — the spec is explicit; it's a temporal ordering constraint
  • SHOULD include a Link with rel="deprecation" pointing to migration docs

The act of sending Deprecation does not change the resource's behavior — it's a signal, not a degradation. Servers SHOULD keep the resource functional through the Sunset date (modulo emergencies).

Recommended timeline

T+0      First Deprecation: header sent in production
T+30d    Public announcement (changelog, email, docs)
T+90d    Add response logs / metrics on usage of deprecated path
T+180d   Sunset date set 90 days out
T+270d   Sunset date arrives — endpoint returns 410 Gone with migration link

For minor breaking changes on a major-version-pinned API: 6 months is the median. For full v1 → v2 sunsets: 12-24 months.


4. The version-transformer pattern

Stripe's internal architecture solves the "we have 50 versions in production" problem:

[The system uses] API resource classes that define current API response structures, combined with version change modules that encapsulate backwards-incompatible transformations. When processing responses, the system walks back through time and applies each version change module that it finds along the way until that target version is reached.

The shape:

// resource: the canonical (latest) representation
const charge = {
  id: 'ch_123',
  amount: 1000,
  payment_method_details: { card: { brand: 'visa', last4: '4242' } },
}

// Each breaking change is one transformer module:
const v_2024_03_01 = {
  // Removes 'card' nesting under payment_method_details, flattens fields
  apply(resource) {
    return {
      ...resource,
      card_brand: resource.payment_method_details?.card?.brand,
      card_last4: resource.payment_method_details?.card?.last4,
    }
  },
}

const v_2023_10_15 = {
  // Renames 'amount' to 'amount_cents'
  apply(resource) {
    const { amount, ...rest } = resource
    return { ...rest, amount_cents: amount }
  },
}

// Pipeline applies transformers in reverse-chronological order
// until requested version is reached
function transform(resource, requestedVersion) {
  const chain = transformers.filter(t => t.date > requestedVersion)
  return chain.reduceRight((r, t) => t.apply(r), resource)
}

The wins:

  • Core code stays modern — every endpoint is written against the latest schema; no if (version < ...) sprinkled through business logic.
  • Each break is one file — easy to review, easy to test in isolation, easy to delete when the last user pins past it.
  • Telemetry — you can count which transformers fire per day to plan deprecations against actual usage.

The cost: building the transformer framework. Worth it if you have >3 breaking changes per year and >10k API consumers; overkill for a <50-customer internal API.


5. Internal vs public APIs

The asymmetry matters:

InternalPublic
Coordinated deploy possible?Yes — atomic swapNo — clients deploy independently
Schema evolutionAdditive + Slack messageStrict versioning + email + dashboards + blog post
Sunset windowDays to weeks6-24 months
Versioning needOften none — just Deprecation headersMandatory
Best strategyAdditive evolution; URI version only on rewriteDate-pinned headers (if scale warrants), URI for major rewrites

The trap: companies treat all APIs as "public" and build the heavy versioning infrastructure for an API used only by 3 internal services. The reverse trap: a leaked internal API has external consumers and you can't actually break it.

Defense: make "public" a binary tag on the service. Public services get the full versioning ceremony; internal services don't, but get aggressive monitoring of who's calling them.


Anti-patterns

Anti-patternWhy it bitesFix
Bumping /v1/ to /v2/ for one breaking changeForces clients to migrate everything for one fieldDate-based version OR additive evolution
Removing endpoints with no Sunset headerClients break with no warning; support tickets explodeAlways pair removal with Deprecation 90+ days prior
Sending Deprecation but no LinkClients know it's deprecated but not what to doAlways include `Link: <migration-doc>; rel="deprecation

Content truncated.

When not to use it

  • For GraphQL schema evolution
  • For gRPC / Protobuf wire-compatibility
  • For database schema versioning

Limitations

  • The skill is not for GraphQL schema evolution.
  • It is not for gRPC / Protobuf wire-compatibility.
  • It is not for database schema versioning.

How it compares

This skill emphasizes date-based pinned versions for public APIs to allow small breaking changes without forcing clients onto a new tree, unlike major-version URI bumps.

Compared to similar skills

api-versioning-strategy side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
api-versioning-strategy (this skill)03moReviewAdvanced
mcp-builder1363moReviewAdvanced
api-design-principles722moNo flagsIntermediate
langchain-architecture82moReviewIntermediate

Try saying

Example prompts that trigger this skill in your AI assistant.

You might also like

mcp-builder

anthropics

Guide for creating high-quality MCP (Model Context Protocol) servers that enable LLMs to interact with external services through well-designed tools. Use when building MCP servers to integrate external APIs or services, whether in Python (FastMCP) or Node/TypeScript (MCP SDK).

136215

api-design-principles

wshobson

Master REST and GraphQL API design principles to build intuitive, scalable, and maintainable APIs that delight developers. Use when designing new APIs, reviewing API specifications, or establishing API design standards.

72170

langchain-architecture

wshobson

Design LLM applications using the LangChain framework with agents, memory, and tool integration patterns. Use when building LangChain applications, implementing AI agents, or creating complex LLM workflows.

899

nodejs-backend-patterns

wshobson

Build production-ready Node.js backend services with Express/Fastify, implementing middleware patterns, error handling, authentication, database integration, and API design best practices. Use when creating Node.js servers, REST APIs, GraphQL backends, or microservices architectures.

1246

springboot-patterns

affaan-m

Spring Boot 架构模式、REST API 设计、分层服务、数据访问、缓存、异步处理和日志记录。适用于 Java Spring Boot 后端工作。

1147

backend-architect

sickn33

Expert backend architect specializing in scalable API design, microservices architecture, and distributed systems. Masters REST/GraphQL/gRPC APIs, event-driven architectures, service mesh patterns, and modern backend frameworks. Handles service boundary definition, inter-service communication, resilience patterns, and observability. Use PROACTIVELY when creating new backend services or APIs.

1014

Search skills

Search the agent skills registry