{
  "uid": "cogitave.learn.api-design.version-and-deprecate",
  "kind": "moduleUnit",
  "href": "/modules/api-design/version-and-deprecate/",
  "title": "Version and deprecate without breaking consumers",
  "summary": "",
  "type": null,
  "products": [],
  "roles": [],
  "levels": [],
  "subjects": [],
  "headings": [
    "1. The right scheme per surface",
    "2. Additive by default, and the support window",
    "3. The deprecation lifecycle",
    "4. One manifest, no drift"
  ],
  "source": "Shaping a good v1 is half the job. The\n[versioning-and-deprecation standard](../../../../standards/docs/standards/api-versioning-and-deprecation.md)\nowns the other half: changing that contract over time without breaking anyone.\nIts governing doctrine is blunt - **never break userspace without consent.** A\npublic contract stays functional until it has been deprecated, sunset, and\nretired through process. There is no silent break.\n\n## 1. The right scheme per surface\n\nThere is no single versioning scheme that fits a library, a wire protocol, and a\ngraph. Cogitave picks per surface and makes the version **observable** so agents\ncan negotiate it:\n\n| Surface | Scheme | Default |\n|---|---|---|\n| Library / SDK | **SemVer 2.0.0** | machine-verified before publish |\n| HTTP API (public) | **date-based, consumer-pinned** (`YYYY-MM-DD`) | no hard cutover |\n| gRPC / Protobuf | **field-number immutability** | additive-only |\n| GraphQL | **continuous evolution**, `@deprecated` | never versioned |\n| Event / JSON schema | **registry compatibility mode** | `BACKWARD_TRANSITIVE` |\n| MCP | **protocol date + per-capability SemVer** | negotiated |\n\nTwo choices are worth internalizing. **HTTP is date-based and consumer-pinned**:\na consumer sends `Cogitave-Version: 2026-06-28`, the server serves that frozen\ncontract and transforms responses forward, so each integration upgrades\ndeliberately by bumping one date instead of being force-migrated. A version is\nnot a URL - it belongs in a header, not in `/v2`. **gRPC rests on one absolute\nrule: a field number is forever.** Never change, reuse, or retype a field number;\n`reserved` a number and its name on removal. Both are gated in CI (`buf breaking`\nfor proto, the manifest and RFC for HTTP).\n\n## 2. Additive by default, and the support window\n\nBackward compatibility within a contract is **absolute** - additive change only.\nAny subtractive or semantics-altering change starts a new contract version and\ndeprecates the old one. Cogitave supports the current contract plus the previous\n(N-1) for a **minimum of 12 months**, and date-pinned HTTP consumers for\n**24 months** from the date they pin. The exact window is declared in the\ndeprecating RFC and encoded in the manifest - never implicit.\n\n## 3. The deprecation lifecycle\n\nA contract moves through a fixed state machine, aligned to the docs moniker so\nthe API and its documentation always agree:\n\n`preview -> active -> deprecated -> sunset -> retired`\n\n`deprecated` still serves and emits signals; `sunset` is the phase after\n`sunsetAt` when removal is imminent; `retired` returns a typed error with a\nmigration link. Announcing `deprecated` to `sunsetAt` is a **minimum 6 months**;\nthe total deprecated window is **at least 12 months**. Removal is gated by\n**both** the calendar (`sunsetAt` passed) **and** observation - you may not retire\na contract you cannot prove is unused, measured by a deprecation-usage metric.\n\n## 4. One manifest, no drift\n\nA deprecated contract MUST emit **every** applicable signal: HTTP `Deprecation`\nand `Sunset` headers, in-schema markers (`@deprecated`, proto `deprecated = true`,\nOpenAPI `deprecated: true`), SDK/CLI warnings, a changelog `Deprecated` entry, and\na migration guide. All of them are **generated from one committed\n[deprecation manifest](../../../../standards/docs/standards/api-versioning-and-deprecation.md)**,\nupdated in the same PR as the change, so headers, docs moniker, and changelog\ncannot disagree.\n\n> [!IMPORTANT]\n> A breaking or deprecating change is a **design-class change**. It requires an\n> Accepted RFC through the request lifecycle before any code. Agents draft the\n> RFC, manifest, and migration guide and open the PR; a human holds the merge and\n> retirement gate - agents propose, they never enact.\n",
  "partOf": "cogitave.learn.api-design",
  "durationInMinutes": 8,
  "quiz": null
}