Advanced (अपडेट: 2/6/2026)

Claude Code के साथ सुरक्षित API versioning की practical guide

Claude Code से API versioning सुरक्षित करें: OpenAPI, compatibility tests, Deprecation headers और rollout guide.

Claude Code के साथ सुरक्षित API versioning की practical 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 APIcustomers अपने calendar से migrate करते हैंURL path या explicit header
Internal microservicesclients को साथ 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 जैसे दिखते हैं।

StyleExampleStrengthCommon pitfall
URL path/api/v1/ordersrouting, docs और debugging साफपुराने paths लंबे समय तक रहते हैं
Custom headerAPI-Version: 2URL stable रहती हैheader भूलना आसान; cache कोVary: API-Version चाहिए
Media typeAccept: application/vnd.acme.orders.v2+jsonHTTP 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 करना।

#Claude Code #API design #API versioning #OpenAPI #TypeScript
मुफ़्त

मुफ़्त PDF: Claude Code cheatsheet

Email डालें और commands, review habits तथा safe workflow वाली एक-page PDF पाएँ.

हम आपका data सुरक्षित रखते हैं और spam नहीं भेजते.

Masa

लेखक के बारे में

Masa

Claude Code workflow और team adoption पर काम करने वाला engineer.