Best Dead Code Detection Tools (Open Source and Commercial)

repowise team··11 min read
best dead code detection toolsfind dead codeunused code detectiondead code toolremove dead code

Best dead code detection tools are only useful if they match the kind of dead code you actually ship. Unreachable files, unused exports, and zombie packages all need different heuristics. A tool that flags one well may miss the others. In this post, I’ll compare open source and commercial options that help you find dead code, explain where each one is strong, and show a cleanup workflow that keeps false positives under control.

What dead code actually is

Dead code is code you can delete without changing behavior. That sounds simple until you look at a real repo. Static analyzers, compilers, and IDE inspections all disagree on what counts as dead, because they make different assumptions about entry points, reflection, config files, and generated code.

Unreachable files

An unreachable file is a file that no runtime path can import or load. In a JS app, this might be a utility module left behind after a refactor. In Python, it might be a helper that nothing imports. In a monorepo, the same file can be reachable from one package and dead from another.

Tools that only read a file in isolation miss this. You need a project graph.

Unused exports

Unused exports are the easiest place to start. These are functions, classes, types, or constants that other files never import.

Knip is explicit here: it focuses on exported values and types across files, not on unused locals inside a file. Its docs also define unused files as project files that are not resolved from entry files, which is the right mental model for codebase-level cleanup. (knip.dev)

Zombie packages

Zombie packages are dependencies that still sit in package.json, requirements.txt, or similar manifests even though the code no longer imports them. This is dead code in supply-chain form. It costs build time, adds security surface, and makes upgrades noisier.

Knip can find unused dependencies by analyzing package manifests, source code, and other config files. That makes it more than an export linter. (knip.dev)

What modern detectors do differently

The old model was a grep-like scan plus a few syntax heuristics. The better tools build a graph.

Confidence scoring

A good dead code tool should tell you how sure it is. Python tools like Vulture mark suspicious code and let you review it manually. Newer analyzers often attach a score or a reason string, which helps you sort the obvious wins from the risky ones.

Repowise’s get_dead_code is built around this idea: it sits on top of graph, git, and context layers, so dead-code findings can be paired with ownership, history, and dependency impact. Its architecture page lists dead code detection alongside PageRank, community detection, and other graph analysis, which is the right shape for larger repos. (repowise.dev)

Cross-language graph traversal

The main reason dead code gets tricky is that modern repos are not single-language. A TypeScript frontend calls a Python API, which shells out to Go, which loads config from YAML. A file can look unused until you include generated code, dynamic imports, and framework conventions.

Repowise’s docs say it supports 14 languages in its graph layer and exposes 7 MCP tools through the Model Context Protocol. MCP itself is a standard for connecting AI apps to external tools and data sources, which makes repo intelligence callable instead of trapped in a UI. (docs.repowise.dev)

1. repowise get_dead_code

Best for: mixed-language repos, large monorepos, teams that want dead-code findings with context

Repowise is open source and self-hostable under AGPL-3.0, which means you can run it inside your own environment and inspect the engine if you need to. The AGPL is designed for network server software and requires modified source availability when you convey the software through a server. (gnu.org)

The part that matters here is not the license. It is the data model. get_dead_code is one of the MCP tools, so an agent can ask for dead-code candidates in the same session as architecture, ownership, risk, and dependency paths. That matters when a cleanup touches shared code. (repowise.dev)

What it does well:

  1. Finds dead code in the context of a dependency graph.
  2. Surfaces ownership and history next to the finding.
  3. Helps you avoid “delete first, ask later” cleanup.

Where it shines:

  • large repos with refactors in flight
  • codebases with dynamic imports or framework entry points
  • teams that want one place to inspect impact before removal

If you want to see the surrounding intelligence, the architecture page shows how the graph, git history, and MCP layer fit together. If you want a concrete output shape, the FastAPI dependency graph demo and auto-generated docs for FastAPI show what the platform produces on a real codebase. You can also see live examples across repos.

Dead Code Detection Comparison TableDead Code Detection Comparison Table

2. Knip

Best for: JavaScript and TypeScript projects

Knip is the strongest open source choice for JS/TS dead-code cleanup. Its docs say it finds unused dependencies, exports, and files, and it builds module and dependency graphs for npm projects. It also has config-aware heuristics, which matters in frameworks where “entry file” is a moving target. (knip.dev)

Why I like it:

  • It knows the difference between unused files and unused exports.
  • It can remove unused files with auto-fix when you are ready.
  • It has enough knobs to fit monorepos and framework-heavy apps. (knip.dev)

The tradeoff is the same one that comes with any smart static analyzer: you must configure entry files correctly. Knip’s docs are blunt about this. If you miss entry points, it will report used code as unused. If you over-broaden entry files, you hide dead code. (knip.dev)

Use Knip when:

  • your repo is mostly TS/JS
  • you want unused dependency detection as part of the same pass
  • you are okay tuning config to reduce false positives

3. ts-prune

Best for: older TypeScript projects with a narrow “unused exports” problem

ts-prune is simpler than Knip. It reads tsconfig and prints unused exports. That narrow focus is its advantage and its limit. It is easier to understand, but it does not try to solve the whole dead-code problem. (npmjs.com)

This makes ts-prune a decent legacy tool when:

  • you only care about unused exports
  • you want a small CLI with minimal setup
  • you already have separate tools for dependencies and file cleanup

I would not start here for a new codebase. Knip covers more ground, and modern repo intelligence tools can give you dependency paths and risk context instead of only a list of names. The ts-prune readme is also a clue to its age: its model is export-centric, not codebase-centric. (github.com)

4. Vulture

Best for: Python projects

Vulture is the classic Python dead-code detector. It scans for unused classes, functions, and variables, and the PyPI docs describe it as a tool for finding unused code in Python programs. It also supports whitelists, exclusions, and common # noqa conventions. (manpages.ubuntu.com)

Why teams still use it:

  • It is lightweight.
  • It is easy to run in CI.
  • It finds obvious dead code before a manual review. (pypi.org)

Where it struggles:

  • dynamic imports
  • decorators and framework magic
  • project-specific conventions that need whitelists

That last point matters. Vulture is best treated as a triage tool, not an automated delete button. You run it, inspect the list, and confirm the candidates before you remove dead code. The docs strongly suggest that workflow by recommending whitelists and scan-path tuning. (pypi.org)

5. Dead-code detection in IDEs

Best for: local edits and small cleanups

IDE inspections are useful, but they are not the whole answer. IntelliJ Platform inspections can flag unused declarations, and JetBrains documents special cases around tests and entry points. That is great for local feedback while you edit. It is not enough for repo-wide cleanup. (intellij-support.jetbrains.com)

Use IDE dead-code detection for:

  • immediate feedback on the file you are editing
  • small refactors
  • quick verification before deletion

Do not use it as your only source of truth if:

  • your app uses reflection
  • entry points are spread across config files
  • you need dependency-wide reachability

Side-by-side comparison

Here is the short version.

ToolBest forFinds unused exportsFinds unused filesFinds unused depsCross-languageNotes
repowise get_dead_codeMixed-language repos, context-aware cleanupYesYesIndirectly via graph/contextYesPairs dead-code findings with ownership, risk, and paths.
KnipJS/TS reposYesYesYesNoStrong config model, best open source JS/TS option.
ts-pruneLegacy TS export cleanupYesLimitedNoNoNarrow, simple, aging tool.
VulturePython reposYesPartlyNoNoGood triage, needs whitelist discipline.
IDE inspectionsLocal editingYesLimitedNoNoBest as a companion, not the main audit tool.

A practical rule:

  • If your repo is JS/TS, start with Knip.
  • If your repo is Python, start with Vulture.
  • If your repo spans multiple languages or needs historical context, use a graph-based tool like repowise.
  • If you only need local hints, use the IDE.

Cross-Language Dead Code GraphCross-Language Dead Code Graph

Cleanup playbook

Dead-code detection fails when teams jump straight to deletion. Use a staged process.

1. Start with one signal type

Pick either unused exports or unused files. Do not start with everything at once. That keeps the review manageable.

2. Sort by confidence

Delete obvious leaf code first:

  • private helpers no one imports
  • stale feature flags
  • abandoned modules with no dependents
  • dead packages with no runtime or build references

3. Check for framework entry points

Watch for:

  • route files
  • reflection-based lookups
  • CLI subcommands
  • plugin registries
  • test-only imports

4. Review ownership and history

Before you remove a shared module, check who owns it and how often it changes. Repowise’s architecture and git-intelligence layers are built for that. Its hotspot and ownership views help you separate old debris from active infrastructure. (repowise.dev)

5. Remove in small patches

Small patches are easier to revert. They also make CI failures obvious. If you use a tool with auto-fix, run it on one package or directory at a time.

6. Add guardrails

After cleanup:

  • keep the detector in CI
  • add a baseline or allowlist for known exceptions
  • review new dead-code hits in PRs

If you want the repo context around the cleanup, the hotspot analysis demo shows how churn and complexity interact, and the ownership map for Starlette shows the kind of history signal that keeps refactors from becoming guesswork.

FAQ

What is the best dead code detection tool for JavaScript?

Knip is the best default for JS and TS. It finds unused files, exports, and dependencies, and it models the project as a dependency graph. (knip.dev)

What is the best dead code detection tool for Python?

Vulture is the standard starting point for Python. It scans for unused classes, functions, and variables, and it is easy to run in CI. (manpages.ubuntu.com)

Can dead code detection tools remove dead code automatically?

Some can, but I would keep auto-removal behind a review step. Knip supports removing unused files with auto-fix, but false positives still happen when entry points are misconfigured. (knip.dev)

How do I find dead code in a monorepo?

Use a tool that understands project graphs and config-based entry points. In JS/TS, Knip is strong here. In mixed-language repos, a graph-based platform like repowise can add dependency paths, ownership, and history on top of the detection pass. (knip.dev)

Is IDE dead code detection enough?

No. IDE inspections are good for local edits and quick feedback, but they do not replace repo-wide analysis. JetBrains documents edge cases around tests and unused declarations, which is exactly why a standalone audit still matters. (intellij-support.jetbrains.com)

Should I use ts-prune in 2026?

Only if you want a narrow unused-export checker for a legacy TypeScript codebase. For new work, Knip covers more of the dead-code surface area. (github.com)

Final recommendation

If you need the shortest path:

  • JS/TS: Knip
  • Python: Vulture
  • Legacy TS exports only: ts-prune
  • Mixed-language or context-heavy repos: repowise get_dead_code
  • Local edit feedback: IDE inspections

If your goal is to remove dead code safely, do not stop at a list of filenames. Pair findings with dependency paths, ownership, and history. That is what turns unused code detection from a cleanup chore into a reliable maintenance routine.

Try repowise on your own repo or start with pip install repowise && repowise init.

Try repowise on your repo

One command indexes your codebase.