| name | deprecation-and-migration |
| description | Manages deprecation and migration. Use when removing old systems, APIs, or features. Use when migrating users from one implementation to another. Use when deciding whether to maintain or sunset existing code. |
Deprecation and Migration
Overview
Code is a liability, not an asset. Every line maintained is a line that can break. Deprecation is the practice of systematically retiring code, APIs, or features that are no longer earning their maintenance cost. Do it deliberately, not by abandonment.
When to Use
- Removing an old API endpoint that has been replaced
- Migrating from one library or pattern to another
- Sunsetting a feature that is no longer used or supported
- Cleaning up dead code identified during a refactor
The Deprecation Mindset
Compulsory vs. Advisory Deprecation:
| Type | Meaning | Example |
|---|
| Compulsory | Will be removed on a specific date — users must migrate | Old /v1/skills endpoint being replaced by /v2/skills |
| Advisory | Discouraged but not being removed yet | A config option that has a better alternative |
Always be explicit about which type you are doing.
The Deprecation Process
Step 1: Document Before Removing
Before removing anything, document:
- What is being deprecated
- Why (the reason, not just "it's old")
- What the replacement is
- When removal will happen (for compulsory deprecation)
import warnings
@router.get("/v1/skills/{repo}")
async def get_skill_v1(repo: str):
"""
DEPRECATED: Use /v2/skills/{repo} instead.
This endpoint will be removed on 2026-09-01.
Migration guide: docs/migration/v1-to-v2.md
"""
warnings.warn(
"GET /v1/skills is deprecated. Use /v2/skills. Removal: 2026-09-01.",
DeprecationWarning,
stacklevel=2,
)
return await get_skill_v2(repo)
Step 2: Add Deprecation Signals
Make deprecation visible to consumers:
For HTTP APIs:
response.headers["Deprecation"] = "true"
response.headers["Sunset"] = "Tue, 01 Sep 2026 00:00:00 GMT"
response.headers["Link"] = '</v2/skills>; rel="successor-version"'
For Python functions:
import warnings
def generate_skill_v1(repo: str) -> str:
warnings.warn(
"generate_skill_v1() is deprecated. Use generate_skill() instead.",
DeprecationWarning,
stacklevel=2,
)
return generate_skill(repo)
For TypeScript/React:
export function SkillExport({ repo }: Props) {
console.warn("SkillExport is deprecated. Use SkillExportV2.");
return <SkillExportV2 repo={repo} />;
}
Step 3: Migrate Callers First
Before removing the deprecated thing:
- Find all callers:
grep -r "generate_skill_v1" api/ web/
- Migrate each one to the replacement
- Verify no callers remain: the grep should return nothing
- Remove the deprecated code
Step 4: Remove Clean
When removing deprecated code:
- Delete the entire file or function — don't leave it commented out
- Remove associated tests for the removed code
- Update any documentation that referenced the removed thing
- Update the CHANGELOG or ADR
Step 5: Remove Dead Code
Dead code is worse than no code — it confuses future engineers and agents:
Finding dead code:
grep -r "def generate_skill_v1" api/ | wc -l
grep -r "generate_skill_v1" api/ | grep -v "def "
npx ts-prune --project web/tsconfig.json
GitScape-Specific Migration Patterns
Migrating a Cloud Run Environment Variable
When renaming or removing a secret in GCP Secret Manager:
- Add the new secret:
gcloud secrets create NEW_SECRET_NAME
- Update
cloudbuild.yaml to inject both old and new: test that it works with new
- Update all code to use the new name
- Verify no code references the old name
- Remove old secret from
cloudbuild.yaml
- Delete the old secret:
gcloud secrets delete OLD_SECRET_NAME
Never delete a secret before confirming no live Cloud Run revision references it.
Common Rationalizations
| Rationalization | Reality |
|---|
| "We might need it later" | Git history exists. Dead code is not a backup strategy. |
| "Let's just leave the old endpoint — it's not hurting anyone" | It is. It's surface area to maintain, test, and secure. |
| "We'll migrate it when we have more time" | Migrations get harder over time, not easier. Callers accumulate. |
| "The deprecated code still works" | Working is not the bar. Maintained, understood, and secure is the bar. |
Red Flags
- Commented-out code with "keeping for reference"
- Functions with
_old, _v1, or _deprecated in the name that are still called
- Deprecated APIs with no removal date
- Code that can't be removed because "we don't know who uses it"
Verification
After completing a deprecation or migration: