Claude Code के साथ सुरक्षित API versioning की practical guide
Claude Code से API versioning सुरक्षित करें: OpenAPI, compatibility tests, Deprecation headers और rollout guide.
API versioning का मतलब सिर्फ route में/v2 जोड़ना नहीं है। यह उन mobile apps, partner integrations, internal services, Webhook consumers और batch jobs से किया गया compatibility promise है जो आपकी API पर पहले से निर्भर हैं। एक response field का नाम बदलना आपको साफ design लग सकता है, लेकिन वही पुराना client तोड़ सकता है।
Claude Code official docs बताते हैं कि Claude Code codebase पढ़ सकता है, files edit कर सकता है और commands चला सकता है। इसलिए यह API migration में उपयोगी है। लेकिन अगर prompt सिर्फ “इस API को modernize करो” कहता है, तो assistant नई shape को साफ करने लगेगा और पुराने consumers भूल सकता है। सुरक्षित workflow में contract, forbidden changes, verification commands और rollout plan पहले दिए जाते हैं।
इस guide में URL path, header और media type versioning की tradeoffs, OpenAPI contract, backward compatibility,Deprecation औरSunset headers, changelog policy, consumer tests, rollout/fallback और Claude Code prompts शामिल हैं। Facts के लिए official sources देखें: OpenAPI Specification, RFC 9745 Deprecation Header, और RFC 8594 Sunset Header।
API implementation के broader flow के लिए Claude Code API development, review checklist के लिए Claude Code code review, और release discipline के लिए Changesets version management भी उपयोगी हैं।
पहले Compatibility Contract लिखें
Versioning का उद्देश्य पुराना code हमेशा रखना नहीं है। उद्देश्य यह है कि consumers अपने समय पर migration कर सकें। Masa ने एक छोटे orders API पर यह pattern test किया। जब prompt सिर्फ “v2 जोड़ो और customer fields rename करो” था, नया dashboard चल गया लेकिन पुराना CSV export fail हो गया। कमी code generation में नहीं थी; कमी rules में थी: v1 response shape रखनी है, deprecation date publish करनी है, consumer test जोड़ना है, और migration guide update करनी है।
तीन common use cases हैं:
| Use case | मुख्य constraint | आमतौर पर सही style |
|---|---|---|
| Mobile apps के लिए public REST API | पुराने app versions महीनों तक रहते हैं | URL path versioning |
| B2B SaaS partner API | customers अपने calendar से migrate करते हैं | URL path या explicit header |
| Internal microservices | clients को साथ upgrade किया जा सकता है | header या media type |
Claude Code से implementation मांगने से पहले current consumers, support window, breaking change की definition और monitoring metrics लिखें। Breaking change सिर्फ route delete करना नहीं है। Response field rename, new required request field, error envelope change, default sort change और pagination shape change भी पुराने clients तोड़ सकते हैं।
URL, Header या Media Type चुनें
Version कहाँ रखी जाती है, इससे routing, caching, documentation, SDK generation और support प्रभावित होते हैं। Public APIs के लिए URL path versioning practical default है। यह logs में दिखती है, API Gateway में आसान है औरcurlसे test करना सरल है। कमी यह है कि resource URI में product version आ जाती है, इसलिए/api/v1/orders/123और/api/v2/orders/123अलग resources जैसे दिखते हैं।
| Style | Example | Strength | Common pitfall |
|---|---|---|---|
| URL path | /api/v1/orders | routing, docs और debugging साफ | पुराने paths लंबे समय तक रहते हैं |
| Custom header | API-Version: 2 | URL stable रहती है | header भूलना आसान; cache कोVary: API-Version चाहिए |
| Media type | Accept: application/vnd.acme.orders.v2+json | HTTP content negotiation के करीब | OpenAPI, SDK और support complexity बढ़ती है |
Media type versioning मेंVary: Acceptभेजें, ताकि cache v1 और v2 responses mix न करे। Header style मेंVary: API-Versionभेजें। URL style में भी अगर response compatibility बदल रही है, तो OpenAPI में v1 और v2 को अलग contracts की तरह लिखें।
OpenAPI को Source Of Truth बनाएं
OpenAPI HTTP API को machine-readable form में लिखता है: paths, methods, parameters, request bodies, responses और security। सरल शब्दों में, implementation से पहले API promise लिखना। openapi field OpenAPI specification version है, औरinfo.versionआपके API document की version है। Claude Code prompt में यह difference साफ लिखें।
नीचे v1 को deprecated रखते हुए v2 जोड़ने का छोटा contract है। Tool compatibility के लिएopenapi: 3.1.0दिया गया है; newer spec अपनाने से पहले official OpenAPI docs और अपनी tooling check करें।
openapi: 3.1.0
info:
title: Acme Orders API
version: 2.0.0
servers:
- url: https://api.example.com
paths:
/api/v1/orders/{orderId}:
get:
operationId: getOrderV1
summary: Get an order in the legacy v1 shape
deprecated: true
x-deprecated-at: "2026-03-31T00:00:00Z"
x-sunset-at: "2026-12-31T23:59:59Z"
parameters:
- name: orderId
in: path
required: true
schema:
type: string
responses:
"200":
description: Legacy order response
headers:
Deprecation:
schema:
type: string
description: RFC 9745 structured date, for example @1774915200
Sunset:
schema:
type: string
description: RFC 8594 HTTP-date when v1 may stop responding
content:
application/json:
schema:
$ref: "#/components/schemas/OrderV1Envelope"
/api/v2/orders/{orderId}:
get:
operationId: getOrderV2
summary: Get an order in the current v2 shape
parameters:
- name: orderId
in: path
required: true
schema:
type: string
responses:
"200":
description: Current order response
content:
application/json:
schema:
$ref: "#/components/schemas/OrderV2Envelope"
components:
schemas:
OrderV1Envelope:
type: object
required: [data]
properties:
data:
type: object
required: [id, customerName, totalCents, currency]
properties:
id:
type: string
customerName:
type: string
totalCents:
type: integer
currency:
type: string
OrderV2Envelope:
type: object
required: [data]
properties:
data:
type: object
required: [id, customer, amount, status]
properties:
id:
type: string
customer:
type: object
required: [displayName]
properties:
displayName:
type: string
amount:
type: object
required: [value, currency]
properties:
value:
type: integer
currency:
type: string
status:
type: string
enum: [paid, shipped]
Claude Code को यह YAML पहले दें, फिर implementation मांगें। Prompt में लिखें: v1 fields remove नहीं करने, v1 status codes नहीं बदलने, और contract बदलने पर tests तथा CHANGELOG भी update करने हैं।
Node में Backward Compatibility लागू करें
यह TypeScript server केवल Node built-in APIs इस्तेमाल करता है। इसेapi-versioning-demo.tsके रूप में save करके URL path,API-Versionheader औरAcceptmedia type तीनों test किए जा सकते हैं। v1 legacy response रखता है, v2 current response देता है, और v1 deprecation headers भेजता है।
import { createServer } from "node:http";
import { parse } from "node:url";
type ApiVersion = "v1" | "v2";
type OrderRow = {
id: string;
customerName: string;
totalCents: number;
currency: "JPY" | "USD";
status: "paid" | "shipped";
createdAt: string;
};
const orders = new Map<string, OrderRow>([
[
"o_100",
{
id: "o_100",
customerName: "Masa Tanaka",
totalCents: 129800,
currency: "JPY",
status: "paid",
createdAt: "2026-06-02T09:00:00.000Z",
},
],
]);
function detectVersion(req: { headers: Record<string, string | string[] | undefined> }, pathname: string) {
const pathVersion = pathname.match(/^\/api\/(v[12])\//)?.[1] as ApiVersion | undefined;
if (pathVersion) return { version: pathVersion, source: "path" };
const header = req.headers["api-version"];
if (typeof header === "string") {
const normalized = header.startsWith("v") ? header : `v${header}`;
if (normalized === "v1" || normalized === "v2") {
return { version: normalized, source: "header" };
}
throw new Error(`Unsupported API-Version: ${header}`);
}
const accept = req.headers.accept;
if (typeof accept === "string") {
const mediaMatch = accept.match(/application\/vnd\.acme\.orders\.v([12])\+json/);
if (mediaMatch) {
return { version: `v${mediaMatch[1]}` as ApiVersion, source: "media-type" };
}
}
return { version: "v1" as ApiVersion, source: "default" };
}
function orderIdFrom(pathname: string) {
return pathname.match(/^\/api\/(?:v[12]\/)?orders\/([^/]+)$/)?.[1];
}
function toV1(row: OrderRow) {
return {
data: {
id: row.id,
customerName: row.customerName,
totalCents: row.totalCents,
currency: row.currency,
},
};
}
function toV2(row: OrderRow) {
return {
data: {
id: row.id,
customer: { displayName: row.customerName },
amount: { value: row.totalCents, currency: row.currency },
status: row.status,
createdAt: row.createdAt,
},
};
}
function addDeprecationHeaders(res: import("node:http").ServerResponse) {
const deprecatedAt = Math.floor(Date.parse("2026-03-31T00:00:00Z") / 1000);
res.setHeader("Deprecation", `@${deprecatedAt}`);
res.setHeader("Sunset", new Date("2026-12-31T23:59:59Z").toUTCString());
res.setHeader(
"Link",
[
'<https://docs.example.com/api/deprecations/v1-to-v2>; rel="deprecation"; type="text/html"',
'<https://docs.example.com/api/sunset-policy>; rel="sunset"; type="text/html"',
].join(", "),
);
}
function sendJson(res: import("node:http").ServerResponse, status: number, body: unknown) {
res.writeHead(status, { "content-type": "application/json; charset=utf-8" });
res.end(JSON.stringify(body, null, 2));
}
const server = createServer((req, res) => {
const pathname = parse(req.url ?? "/").pathname ?? "/";
const orderId = orderIdFrom(pathname);
if (!orderId) {
return sendJson(res, 404, { error: "not_found", message: "Route not found" });
}
let detected: ReturnType<typeof detectVersion>;
try {
detected = detectVersion(req, pathname);
} catch (error) {
return sendJson(res, 400, {
error: "unsupported_version",
message: error instanceof Error ? error.message : "Unsupported API version",
supportedVersions: ["v1", "v2"],
});
}
const row = orders.get(orderId);
if (!row) {
return sendJson(res, 404, { error: "order_not_found", orderId });
}
res.setHeader("Vary", "Accept, API-Version");
res.setHeader("X-API-Version", detected.version);
res.setHeader("X-API-Version-Source", detected.source);
if (detected.version === "v1") {
addDeprecationHeaders(res);
return sendJson(res, 200, toV1(row));
}
return sendJson(res, 200, toV2(row));
});
const port = Number(process.env.PORT ?? 18080);
server.listen(port, () => {
console.log(`API versioning demo: http://localhost:${port}`);
});
npm init -y
npm install -D tsx typescript @types/node
PORT=18080 npx tsx api-versioning-demo.ts &
SERVER_PID=$!
sleep 1
curl -i http://localhost:18080/api/v1/orders/o_100
curl -i -H "API-Version: 2" http://localhost:18080/api/orders/o_100
curl -i -H "Accept: application/vnd.acme.orders.v2+json" http://localhost:18080/api/orders/o_100
kill "$SERVER_PID"
मुख्य बात transformer layer है। v1 को v2 response reuse करके पुराने clients से tolerance की उम्मीद नहीं करनी चाहिए। हर version internal data से अपने public contract में map होना चाहिए।
Deprecation Headers और Version Policy Publish करें
पुराने examples मेंDeprecation: trueमिलता है। RFC 9745 मेंDeprecationstructured Date value है, जैसे@1774915200। RFC 8594 मेंSunsetHTTP-date है, जो बताता है कि resource उस समय के बाद respond करना बंद कर सकता है। Headers runtime signal हैं; migration plan अलग से चाहिए।
Policy repository में रखें:
currentApiVersion: v2
minimumSupportWindowMonths: 12
breakingChangeRequires:
- new-major-version
- migration-guide
- consumer-test
- owner-approval
deprecatedVersions:
- version: v1
deprecatedAt: "2026-03-31T00:00:00Z"
sunsetAt: "2026-12-31T23:59:59Z"
replacement: "/api/v2/orders/{orderId}"
migrationGuide: "https://docs.example.com/api/deprecations/v1-to-v2"
CHANGELOG में added, changed, deprecated और planned removal अलग लिखें। अच्छा entry बताता है कि कौन affected है, क्या बदलना है, replacement endpoint कौन सा है और पुरानी version कब बंद हो सकती है।
Consumer Test से Breaking Change रोकें
Consumer test बताता है कि client अभी भी क्या expect करता है। जब Claude Code duplicate दिखने वाली transformation layer clean करने लगे, तब यह test guard बनता है। नीचे test v1 मेंcustomerNameकी मौजूदगी और v2 केcustomerobject की अनुपस्थिति check करता है।
import assert from "node:assert/strict";
import test from "node:test";
const baseUrl = process.env.API_BASE_URL ?? "http://localhost:18080";
test("v1 keeps the legacy response shape", async () => {
const res = await fetch(`${baseUrl}/api/v1/orders/o_100`);
assert.equal(res.status, 200);
assert.match(res.headers.get("deprecation") ?? "", /^@\d+$/);
assert.match(res.headers.get("sunset") ?? "", /GMT$/);
const body = await res.json();
assert.equal(body.data.customerName, "Masa Tanaka");
assert.equal(body.data.customer, undefined);
});
test("v2 returns the current response shape", async () => {
const res = await fetch(`${baseUrl}/api/orders/o_100`, {
headers: { "API-Version": "2" },
});
assert.equal(res.status, 200);
const body = await res.json();
assert.equal(body.data.customer.displayName, "Masa Tanaka");
assert.equal(body.data.amount.currency, "JPY");
assert.equal(body.data.customerName, undefined);
});
PORT=18080 npx tsx api-versioning-demo.ts &
SERVER_PID=$!
sleep 1
API_BASE_URL=http://localhost:18080 node --test version-contract.test.mjs
kill "$SERVER_PID"
OpenAPI lint भी उसी verification path में जोड़ें:
npx @redocly/cli lint openapi.yaml
Prompt में ये commands हों तो Claude Code को success criteria साफ मिलते हैं। “compatible रखना” अकेला वाक्य बहुत अस्पष्ट है।
Rollout और Fallback तैयार रखें
API versioning failures अक्सर predictable होते हैं। Team DB schema और response shape एक ही deploy में बदल देती है, rollback मुश्किल हो जाता है। Sunset date v1 traffic देखे बिना announce होती है। SDK update होता है, लेकिन raw HTTP users भूल जाते हैं। Documentation deprecated कहती है, पर metrics और alerts remaining consumers नहीं दिखाते।
Rollout को phases में बांटें: v2 add करें, v1 deprecation headers add करें, version usage measure करें, migration guide publish करें, SDK update करें, partners notify करें, sunset enforce करें, और अंत में v1 हटाएं। Fallback में साबित करें कि v2 बंद होने पर v1 चलता है, पुराने clients new fields ignore करते हैं, और DB migration कम से कम read compatibility रखती है।
mkdir -p tmp/version-snapshots
BASE_URL=${BASE_URL:-http://localhost:18080}
for order_id in o_100 missing; do
curl -sS -D "tmp/version-snapshots/${order_id}.v1.headers" \
"$BASE_URL/api/v1/orders/$order_id" \
> "tmp/version-snapshots/${order_id}.v1.json" || true
curl -sS -D "tmp/version-snapshots/${order_id}.v2.headers" \
-H "API-Version: 2" \
"$BASE_URL/api/orders/$order_id" \
> "tmp/version-snapshots/${order_id}.v2.json" || true
done
इन snapshots को PR में attach करें या Claude Code को देकर compatibility summary बनवाएं। यह tests को replace नहीं करता, लेकिन behavior difference reviewer को दिखाता है।
Breaking Changes रोकने वाले Claude Code Prompts
Claude Code को task के साथ contract, forbidden changes और checks दें।
Existing API में v2 जोड़ें। OpenAPI files को source of truth मानें। v1 response shape, status codes और deprecation headers न बदलें।
Edit करने से पहले list करें:
- possible breaking changes
- v1 में रहने वाले fields
- v2 में added या changed fields
- consumer tests जो add होंगे
Edit के बाद चलाएं:
- npm test
- npx @redocly/cli lint openapi.yaml
- curl से v1 और v2 responses compare करें
Final response में compatibility risk, migration guide notes और rollback steps शामिल करें।
Merge से पहले review prompt चलाएं:
इस diff को API compatibility review की तरह देखें।
Check करें:
- v1 required response fields हटे, rename हुए या type change हुए हैं या नहीं
- error envelope, HTTP status, pagination और sort order unexpected बदले हैं या नहीं
- Deprecation, Sunset, Link और Vary headers policy से match करते हैं या नहीं
- OpenAPI, implementation, tests और CHANGELOG aligned हैं या नहीं
- rollback v1 consumers नहीं तोड़ेगा
Findings में file names और concrete fixes दें।
इन prompts से Claude Code का goal “code clean करना” नहीं, “public contract protect करना” बनता है। API work में यही फर्क सबसे ज्यादा मायने रखता है।
निष्कर्ष
Safe API versioning contract से शुरू होती है। Consumers और infrastructure के आधार पर URL, header या media type चुनें। OpenAPI में v1 और v2 दोनों लिखें, explicit transformations रखें,DeprecationऔरSunsetsignals publish करें, actionable CHANGELOG लिखें और refactor से पहले consumer tests चलाएं।
अगर आपकी team Claude Code को API development workflow में लाना चाहती है, तो Claude Code consultation and training API contracts, CI gates, review prompts और rollout checklist को repeatable process में बदलने में मदद कर सकता है। छोटे start के लिए free cheatsheet और इस लेख के prompts को अपने छोटे API पर आजमाएं।
ऊपर वाले Node server से pattern verify किया गया: v1 और v2 same internal row share कर सकते हैं, फिर भी अलग public shapes रख सकते हैं। Consumer test field rename तुरंत पकड़ता है। सबसे आसानी से छूटने वाली चीजें थीं RFC 9745 काDeprecationDate format, header/media-type versioning मेंVary, और OpenAPI, code, tests, CHANGELOG को साथ review करना।
मुफ़्त PDF: Claude Code cheatsheet
Email डालें और commands, review habits तथा safe workflow वाली एक-page PDF पाएँ.
हम आपका data सुरक्षित रखते हैं और spam नहीं भेजते.
लेखक के बारे में
Masa
Claude Code workflow और team adoption पर काम करने वाला engineer.
संबंधित लेख
Claude Code Permission Receipt Pattern: scope, proof और rollback लिखना
Claude Code के लिए permission receipt: allowed actions, approval boundary, verification commands, rollback note और revenue CTA checks।
Claude Code और Codex के लिए सुरक्षित Agent Harness: permissions, verification और rollback
Claude Code और Codex agents के लिए सुरक्षित harness: permissions, plan, verification और rollback.
Claude Code Subagents गाइड: article और code work को सुरक्षित तरीके से delegate करें
Claude Code subagents से article और code work बांटें: delegation rules, prompts, pitfalls, checklist और examples.