Activated Cloud
← App Store

Delete Dead Code

Activated Cloud✓ Officialactivated/delete-dead-code

No ratings yet8 installsv1.0.0Updated Oct 6, 2026● Unknown

Free · MIT

About

Finds and removes code nothing uses (unused functions, files, exports, endpoints, dependencies, config and finished feature flags) with proof for each removal: static analysis to find candidates, full-text search for dynamic and cross-repo references, git history for intent, runtime evidence where available, and a green build after every batch. Risky removals such as public APIs, endpoints and database columns wait for the owner. Use for codebase cleanups, shrinking bundles, dropping old features or removing a dependency. Not for restructuring live code (use refactor-safely).

Software Development

Documentation

From SKILL.md · v1.0.0 · what the agent reads when it loads this skill2 files: SKILL.md, references/dead-code-tools.md

Delete Dead Code

Dead code costs reading time, build time, security patches and wrong turns during debugging. Removing it is one of the best cleanups there is, and one of the easiest ways to break production, because "nothing uses this" is often only true of the references a tool can see. Every removal here comes with proof, is grouped with similar removals, and is followed by a green build.

When to use

  • "Clean out unused code", "remove the old v1 export", "drop this dependency", "the bundle is too big".
  • After a feature flag has been fully rolled out and the old path is no longer needed.
  • After a migration to a new library, to remove the old one.

What you need

  • The repo on its own branch, with a green baseline for tests, lint, type checks and build.
  • Knowledge of who else consumes this code: other services, other repos, mobile apps, customers' integrations, scheduled jobs. Ask the owner if this is a library or exposes an API.
  • Optional runtime evidence: request logs or metrics for endpoints, feature-flag evaluation counts, job run history (via the owner's connected apps or browser).

Method

  1. Collect candidates with tools. Run the analysers that fit the stack (commands and caveats in references/dead-code-tools.md):

    • Python: vulture, ruff check --select F401,F841, deptry for unused dependencies.
    • JavaScript and TypeScript: knip (unused files, exports and dependencies), tsc --noUnusedLocals --noUnusedParameters, ESLint no-unused-vars.
    • Go: staticcheck (U1000), deadcode ./....
    • Rust: compiler dead_code warnings, cargo machete for unused dependencies. Treat every result as a candidate, not a verdict.
  2. Prove each candidate is dead. For each one, search beyond what the analyser sees:

    • search_files for the exact name across the whole repo, including non-code files: templates, YAML, JSON, SQL, Markdown, shell scripts, CI files, Dockerfiles, infrastructure code.
    • Dynamic references: string-built names (getattr(obj, f"handle_{kind}"), require(path), import(\./${name}`)), reflection, decorators that register handlers, dependency-injection containers, serializers, signal and event handlers, routes discovered by convention, plugin entry points in pyproject.tomlorpackage.json`, CLI entry points.
    • Framework conventions: files loaded by naming convention (migrations, fixtures, pages and routes directories, management commands, test helpers).
    • Outside this repo: public library exports, API endpoints, webhooks, queue consumers, cron jobs, mobile clients on old versions, other services. Ask the owner or the teams involved; search other repos you have access to.
    • Runtime: for endpoints and jobs, check logs or metrics for recent calls over a long enough window (at least one full business cycle, often 30 to 90 days).
  3. Check intent. git log -S "<name>" --oneline shows when it was added and removed elsewhere; read the commit. Code added last week for an upcoming feature is not dead, it is early. Code marked deprecated with a removal date is ready when the date has passed.

  4. Classify each candidate.

    Class Examples Rule
    Safe Private functions, unused imports and locals, unreferenced private files, the compiler or type checker confirms Remove in batches
    Careful Exported but unreferenced inside the repo, test helpers, config keys, CSS classes Remove after the full search in step 2 comes back empty
    Risky Public library API, HTTP endpoints, webhooks, database tables and columns, environment variables used in deployments, CLI flags, anything another system may call Deprecate first (log a warning, announce, set a date) and remove only with the owner's go-ahead
  5. Remove in batches by category. Unused imports in one commit, unused private functions in another, a whole dead module in its own. With each removal, also remove its tests, fixtures, docs, config entries and now-unused imports. After each batch run the type checker, tests and build, then commit (chore: remove unused <what>). If something breaks, restore that batch and look for the reference you missed.

  6. Remove dependencies through the package manager, so the lockfile stays consistent: npm uninstall pkg, pnpm remove pkg, uv remove pkg or poetry remove pkg, go mod tidy after removing imports, cargo remove pkg. Then do a clean install and full build to confirm nothing imported it transitively by accident.

  7. Retire feature flags properly. A flag that has been fully on for long enough (the team's rule, often two or more weeks with no issues): remove every check of the flag, keep the winning branch, delete the losing branch and its tests, then remove the flag definition from the flag service only after the code without it is deployed. Ask before deleting anything in a flag service the owner manages.

  8. Database objects last, and never without a go-ahead. Dropping a column or table destroys data. The order is: stop writing it, stop reading it, deploy, wait, back up, then drop in a separate migration that the owner approves.

  9. Verify and measure. Full suite, lint, type checks and build against the baseline. Record what was removed: git diff --shortstat <base>...HEAD, and for frontends the bundle size before and after.

Output

A branch of batched removal commits and a list with three parts:

  • Removed: item, kind, proof (searches run and empty, analyser hit, runtime evidence).
  • Kept: item and why (dynamic reference found, external consumer, too recent).
  • Needs a decision: risky items with the deprecation plan you propose. Plus lines removed, dependencies removed, and the checks run. For example:
Removed (4 commits, -1,240 lines, 2 dependencies):
- `src/legacy/pdf_v1.py` (module): no imports, no string references, knip and vulture agree; last used by the v1 export removed in 3f2a1c0.
- `formatCurrencyOld()` (function): 0 references in code, templates or docs.
- `moment` and `left-pad` (dependencies): no imports after the above; clean install and build pass.

Kept:
- `handle_refund_v1` looks unused but is registered by name in `events/registry.py:88` (`getattr(handlers, f"handle_{kind}_v1")`).

Needs a decision:
- `GET /api/v1/export`: no calls in 90 days of access logs. Propose: return a Sunset header for 30 days, then remove.

Checks: full suite 418 passed; lint, types and build clean after each batch.

Checks before you finish

  • Every removed item has a recorded proof beyond "the tool said so".
  • Nothing in the risky class was removed without the owner's go-ahead.
  • Tests, docs and config for removed code were removed too.
  • Lockfiles were updated by the package manager, and a clean install builds.
  • Full suite, lint, type checks and build are green against the baseline after the last batch.

Pitfalls

  • Trusting the analyser. Tools miss dynamic imports, reflection, registration by decorator and other repos. Search the string.
  • Deleting someone else's API. "Unused in this repo" says nothing about clients. Public surfaces need deprecation first.
  • Too-short runtime windows. A monthly billing job or a yearly report is called rarely, not never.
  • Removing code added for upcoming work. Check the history and ask.
  • One giant removal commit. When something breaks, you cannot tell which removal did it.
  • Orphaned leftovers. Tests, fixtures, docs, env vars and config for the removed code linger and confuse the next reader.
  • Dropping data with code. Database objects follow their own careful sequence.

Versions

v1.0.0currentOct 6, 2026

Listed from the source repository.

Reviews

No reviews yet. Be the first.

Write a review