$npx -y skills add jmxt3/gitscape.ai --skill deprecation-and-migrationManages 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.
| 1 | # Deprecation and Migration |
| 2 | |
| 3 | ## Overview |
| 4 | |
| 5 | 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. |
| 6 | |
| 7 | ## When to Use |
| 8 | |
| 9 | - Removing an old API endpoint that has been replaced |
| 10 | - Migrating from one library or pattern to another |
| 11 | - Sunsetting a feature that is no longer used or supported |
| 12 | - Cleaning up dead code identified during a refactor |
| 13 | |
| 14 | ## The Deprecation Mindset |
| 15 | |
| 16 | **Compulsory vs. Advisory Deprecation:** |
| 17 | |
| 18 | | Type | Meaning | Example | |
| 19 | |---|---|---| |
| 20 | | **Compulsory** | Will be removed on a specific date — users must migrate | Old `/v1/skills` endpoint being replaced by `/v2/skills` | |
| 21 | | **Advisory** | Discouraged but not being removed yet | A config option that has a better alternative | |
| 22 | |
| 23 | Always be explicit about which type you are doing. |
| 24 | |
| 25 | ## The Deprecation Process |
| 26 | |
| 27 | ### Step 1: Document Before Removing |
| 28 | |
| 29 | Before removing anything, document: |
| 30 | - What is being deprecated |
| 31 | - Why (the reason, not just "it's old") |
| 32 | - What the replacement is |
| 33 | - When removal will happen (for compulsory deprecation) |
| 34 | |
| 35 | ```python |
| 36 | # api/app/routes/skills_v1.py |
| 37 | |
| 38 | import warnings |
| 39 | |
| 40 | @router.get("/v1/skills/{repo}") |
| 41 | async def get_skill_v1(repo: str): |
| 42 | """ |
| 43 | DEPRECATED: Use /v2/skills/{repo} instead. |
| 44 | This endpoint will be removed on 2026-09-01. |
| 45 | Migration guide: docs/migration/v1-to-v2.md |
| 46 | """ |
| 47 | warnings.warn( |
| 48 | "GET /v1/skills is deprecated. Use /v2/skills. Removal: 2026-09-01.", |
| 49 | DeprecationWarning, |
| 50 | stacklevel=2, |
| 51 | ) |
| 52 | # Delegate to v2 during transition period |
| 53 | return await get_skill_v2(repo) |
| 54 | ``` |
| 55 | |
| 56 | ### Step 2: Add Deprecation Signals |
| 57 | |
| 58 | Make deprecation visible to consumers: |
| 59 | |
| 60 | **For HTTP APIs:** |
| 61 | ```python |
| 62 | # Add Deprecation and Sunset headers per RFC 8594 |
| 63 | response.headers["Deprecation"] = "true" |
| 64 | response.headers["Sunset"] = "Tue, 01 Sep 2026 00:00:00 GMT" |
| 65 | response.headers["Link"] = '</v2/skills>; rel="successor-version"' |
| 66 | ``` |
| 67 | |
| 68 | **For Python functions:** |
| 69 | ```python |
| 70 | import warnings |
| 71 | |
| 72 | def generate_skill_v1(repo: str) -> str: |
| 73 | warnings.warn( |
| 74 | "generate_skill_v1() is deprecated. Use generate_skill() instead.", |
| 75 | DeprecationWarning, |
| 76 | stacklevel=2, |
| 77 | ) |
| 78 | return generate_skill(repo) |
| 79 | ``` |
| 80 | |
| 81 | **For TypeScript/React:** |
| 82 | ```typescript |
| 83 | /** @deprecated Use SkillExportV2 instead. Will be removed in v2.0. */ |
| 84 | export function SkillExport({ repo }: Props) { |
| 85 | console.warn("SkillExport is deprecated. Use SkillExportV2."); |
| 86 | return <SkillExportV2 repo={repo} />; |
| 87 | } |
| 88 | ``` |
| 89 | |
| 90 | ### Step 3: Migrate Callers First |
| 91 | |
| 92 | Before removing the deprecated thing: |
| 93 | - Find all callers: `grep -r "generate_skill_v1" api/ web/` |
| 94 | - Migrate each one to the replacement |
| 95 | - Verify no callers remain: the grep should return nothing |
| 96 | - Remove the deprecated code |
| 97 | |
| 98 | ### Step 4: Remove Clean |
| 99 | |
| 100 | When removing deprecated code: |
| 101 | - Delete the entire file or function — don't leave it commented out |
| 102 | - Remove associated tests for the removed code |
| 103 | - Update any documentation that referenced the removed thing |
| 104 | - Update the CHANGELOG or ADR |
| 105 | |
| 106 | ### Step 5: Remove Dead Code |
| 107 | |
| 108 | Dead code is worse than no code — it confuses future engineers and agents: |
| 109 | |
| 110 | ```python |
| 111 | # BAD: Commented-out code left "just in case" |
| 112 | # def old_generate_skill(repo): |
| 113 | # # This was the old way, keeping for reference |
| 114 | # ... |
| 115 | |
| 116 | # GOOD: Delete it. Git history is the reference. |
| 117 | ``` |
| 118 | |
| 119 | **Finding dead code:** |
| 120 | ```bash |
| 121 | # Python: find unreferenced functions |
| 122 | grep -r "def generate_skill_v1" api/ | wc -l # should be 1 (the definition) |
| 123 | grep -r "generate_skill_v1" api/ | grep -v "def " # should be 0 callers |
| 124 | |
| 125 | # TypeScript: find unused exports |
| 126 | npx ts-prune --project web/tsconfig.json |
| 127 | ``` |
| 128 | |
| 129 | ## GitScape-Specific Migration Patterns |
| 130 | |
| 131 | ### Migrating a Cloud Run Environment Variable |
| 132 | |
| 133 | When renaming or removing a secret in GCP Secret Manager: |
| 134 | |
| 135 | 1. Add the new secret: `gcloud secrets create NEW_SECRET_NAME` |
| 136 | 2. Update `cloudbuild.yaml` to inject both old and new: test that it works with new |
| 137 | 3. Update all code to use the new name |
| 138 | 4. Verify no code references the old name |
| 139 | 5. Remove old secret from `cloudbuild.yaml` |
| 140 | 6. Delete the old secret: `gcloud secrets delete OLD_SECRET_NAME` |
| 141 | |
| 142 | Never delete a secret before confirming no live Cloud Run revision references it. |
| 143 | |
| 144 | ## Common Rationalizations |
| 145 | |
| 146 | | Rationalization | Reality | |
| 147 | |---|---| |
| 148 | | "We might need it later" | Git history exists. Dead code is not a backup strategy. | |
| 149 | | "Let's just leave the old endpoint — it's not hurting anyone" | It is. It's surface area to maintain, test, and secure. | |
| 150 | | "We'll migrate it when we have more time" | Migrations get harder over time, not easier. Callers accumulate. | |
| 151 | | "The deprecated code still works" | Worki |