Contents
Dead code is the only class of defect that is invisible at runtime. Nothing crashes, no test fails, no alert fires — the code simply sits there, inflating every future read of the file. Removing it is a high-ratio maintenance win, perpetually deferred because proving a symbol unused costs more than ignoring it.
The Dead Code Detector is a single-file browser tool that front-loads that proof. Paste a source file and it returns a risk-ranked deletion queue: unused exports, orphan functions, stale imports, unreachable statements, and transitively dead helpers. It runs entirely client-side with deterministic heuristics — no model, no network call, no upload. That constraint is the point, and it also sets the ceiling on what the tool can honestly claim.
Three ways dead code hides
Mature codebases rarely contain one obvious dead function. They contain three separate failure modes, each requiring a different detection strategy:
- Never-referenced symbols. A helper was written, its call site was refactored away, and the definition stayed. Tooling for this is old and settled — ESLint’s
no-unused-varsand@typescript-eslint/no-unused-varshave flagged it for years, and Python’s vulture does the equivalent for.pyfiles. - Unreachable code. Statements after
return,raise,throw,break, orcontinueinside the same block can never execute. ESLint’sno-unreachableand Pylint’sunreachabletreat this as a correctness smell rather than a cleanup task. - Transitive deadness. This is the one that defeats single-pass linters. If
parseConfigis unused, thennormalizeKeys— referenced only from insideparseConfig— is also dead, but a naive scan sees a reference and stays silent.
What the detector actually measures
The tool parses the pasted file without an AST library (it must run with zero dependencies), so the heuristics are token- and indentation-based rather than grammar-based:
- Unused exports —
exportprefixes,export { … }lists,export default,module.exports/exports.xassignments, and Python__all__entries plus module-level public names. - Orphan functions, methods, and classes — declared names with zero references outside their own declaration span.
- Orphan types — TypeScript
interface Xandtype X =aliases. TypeScript’s ownnoUnusedLocalscovers the variable case; unused exported types usually survive because they are exported. - Dead-code cascade — a fixpoint pass that marks symbols referenced only from already-dead bodies as transitively dead. The pass deliberately never propagates through an export, because an exported symbol may be consumed by another file.
- Unreachable statements — indentation-aware for Python, brace-depth-aware for JS/TS, stopping at dedent,
},case,default,else,except, andfinally. - Statically dead branches —
if False:/while False,if (false | 0 | null | undefined), and theelseclause afterif True. - Stale imports — Python
import/from … import(with aliases and parenthesised multi-line forms), ESM default/named/namespace/import type, and CommonJSrequireincluding destructuring.
Two implementation details matter for accuracy. First, a masking pass blanks out comment and string bodies before reference counting, so an identifier mentioned in a docstring does not register as a use — quoting the symbol in a comment is not a call site. Second, that same masking is used inversely: when a dead symbol’s name appears inside a string or comment (a dynamic reference, a getattr by name), the finding’s risk is escalated from low to high, because a static scan cannot follow a string into a lookup table.
Each finding carries an estimated LOC cost, a source line range, and a risk level — high for decorated or explicitly exported declarations, medium for public Python names and methods, low for cascade hits. Reads flow into a grouped deletion queue, a line-map heat strip, and a copyable Markdown report.
How to use it
Open /tools/dead-code-detector/, pick the language hint, and paste one file. Press Scan (or Ctrl+Enter). The tool auto-scans the bundled Python sample on load. Work it top-down: high-risk items deserve a repository-wide grep before deletion; low-risk cascade items are safe once their root is gone. Copy the Markdown report into a cleanup PR’s description.
Everything runs in the tab. The page makes no requests after load, which is why it is safe to paste proprietary code.
Known limits
This is a static, single-file heuristic — not a semantic analyzer, and not tsc with whole-program type information. Concretely:
- Single-file scope. A symbol exported from the pasted file and consumed elsewhere will be flagged as a candidate. Treat export findings as “confirm with a repo-wide grep”, which is exactly why they are rated high-risk.
- No type resolution. A reference through a computed member (
obj[methodName]) is invisible to it. - No dynamic dispatch. Reflection, decorators that register handlers, and string-keyed registries cannot be followed.
- Regex, not a parser. Pathological or unbalanced input degrades the tokenizer; the tool reports a parse warning rather than guessing silently.
Tree shaking and bundlers remove a narrower, provable slice of unused code at build time; this tool is a pre-commit funnel that tells a human where to look, and it should be read that way. Python users can cross-check its output against ast-based analyzers like vulture.
On the roadmap
Dead Code Detector moves from Planned to Complete on the tools roadmap, the fourth of eighteen planned tools. Next in the Code Analysis category is the Code Churn Forecaster. If the heuristics misjudge a file, the failure mode is documented in-page under // known-limits.




