About kill-legacy
A coding skill focused on removing legacy and fallback paths while keeping the current implementation as the canonical path. It guides developers through evidence-based scope setting, complete removal, and targeted verification without unrelated refactoring.
Key features
- 01Find legacy and fallback candidates
- 02Assess usage and removal risk
- 03Migrate callers to the current path
- 04Remove flags, adapters, and old tests
- 05Run targeted checks and searches
Use cases
- 01Remove deprecated API branches
- 02Retire obsolete feature flags and adapters
- 03Clean up migration leftovers without unrelated refactors
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ dobroslavradosavljevic/ skills --skill kill-legacyAfter installing, type/kill-legacyin Claude Code to confirm it loaded.
Bundled files
References and scripts installed along with SKILL.md.
agents/openai.yamlreferences/legacy-patterns.mdremoval-checklist.mdDetails
From the project READMEKill Legacy
Single goal: eliminate legacy and fallback code so the codebase has one current path, not old + new forever.
Do not broaden into general smell cleanup, renaming, or unrelated refactors. If code is not legacy/fallback (or required to finish removing it), leave it alone.
Core Rules
- Prefer deleting the old path over wrapping, flagging, or “cleaning up later.”
- The current implementation wins. Do not preserve dead dual paths “just in case” without evidence they are still required.
- Preserve public behavior and external contracts unless the user authorizes a breaking change. When a legacy path is the public contract, migrate callers or get explicit approval before removal.
- Prove a path is unused or replaceable before deleting it (references, feature flags, env defaults, runtime config, docs, tests).
- Remove the scaffolding that kept legacy alive: flags, env vars, adapters, dual-read/dual-write, temporary branches, and tests that only exist for the old path.
- Do not replace a removed fallback with a new silent fallback. Fail loudly at the boundary if input is invalid for the current model.
- Preserve unrelated user changes. Keep passes reviewable.
Establish Scope
- Targeted: only the named file, directory, module, feature, flag, or migration leftover. Change outside files only when required to finish the removal.
- Full codebase: search first-party code for legacy/fallback hotspots. Exclude generated files, vendored code, build output, caches, lockfiles, and applied migration history on disk unless they are the stated problem. New migration code that drops legacy columns/tables is allowed when that is the cleanup.
If whether a path is still required is ambiguous and removal would break production or public API, ask one focused question. Otherwise state the assumption and proceed with evidence.
What Counts As Legacy / Fallback
Treat as kill targets when they exist to support an old world alongside the new one:
- Deprecated APIs, dual implementations, compatibility shims, adapters “for old clients”
- Feature flags / env toggles whose default is permanently “new” and old branch is dead
- Dual-read / dual-write, transitional caches, temporary redirects, polyfills no longer needed for supported runtimes
- Commented-out old implementations,
#ifdef-style dead branches,TODO: remove after migration - Fallback values/branches that mask missing migration (
|| oldField,?? legacyDefault,anycasts to keep old shapes alive) - Tests, fixtures, docs, and types that only exercise or describe the old path
Catalog patterns: legacy-patterns.md.
Workflow
1. Find candidates
Search for signals in legacy-patterns.md. For each candidate record:
- Location
- What old behavior it preserves
- What the canonical current path is
- Evidence it is still required vs safe to remove
- Removal risk (public API, data format, clients, flags still on in prod)
2. Decide keep vs kill
| Decision | When |
|---|---|
| Kill now | No remaining callers/config; flag default forever new; supported clients all on new path; user authorized break |
| Kill with migration | Callers/data still on old path but user wants cleanup — migrate then delete in the same effort |
| Keep (report only) | Still required for supported clients, active rollback, or legal/compliance retention — list owner and exit criteria |
Do not leave “soft” legacy (commented code, unused flag, empty shim file). Either remove it or document why it must stay with a concrete exit condition.
3. Remove completely
For each kill:
- Switch all remaining consumers to the canonical path (or delete consumers that only existed for legacy).
- Delete the legacy implementation, types, exports, flags, env plumbing, and adapters.
- Delete or rewrite tests so they assert only current behavior.
- Update docs/comments that describe the old path.
- Remove re-exports and barrels that existed only to expose legacy names.
- Do not leave stub functions that throw “deprecated” unless the user requires a temporary hard break for external clients — prefer compile-time removal for internal code.
Details: removal-checklist.md.
4. Verify
Run the narrowest relevant typecheck, lint, tests, and build for touched surfaces. Grep once more for the removed names/flags to ensure no stragglers.
If verification cannot run, say what was skipped.
Completion Report
- Scope inspected
- Removed: path/flag/shim → canonical replacement
- Kept (with exit criteria): anything still required
- Stragglers searched for
- Verification run and results