Learn

What is dead code?

Dead code is code that exists in your codebase but is never executed, never imported, and never called. It ships with every deployment, gets maintained by your team, and does absolutely nothing. The average codebase carries 10–30% of it.

Five types of dead code.

Dead code isn't just unreachable statements. It exists at every layer of your system.

01

Dead Files

Entire files that are never imported by any other file in the codebase. They exist on disk, they ship with every deployment, and nothing uses them. Often the result of features that were abandoned mid-development or refactors that moved logic elsewhere without deleting the original.

02

Unused Functions

Functions and methods that are defined but never called from any live code path. The function signature is there, the implementation is there, but no execution path reaches it. These are especially common after API changes — the old handler stays defined even after the route is removed.

03

Unreachable Code

Code inside a function that can never execute — typically after an unconditional return statement, inside an always-false conditional branch, or in an exception handler for an exception that can never be thrown. This is the type most linters catch.

04

Orphaned Database Tables

Tables defined in your database schema but never queried, inserted into, or referenced by any application code. The table exists, it may even contain data, but no code path reads from or writes to it. These accumulate during schema migrations that add tables but never clean up the old ones.

05

Ghost Methods

The inverse of dead code: symbols that are called but never defined anywhere. Something in your code references a function that doesn't exist. These are latent runtime errors — they won't crash until execution reaches that code path, and they may go undetected for months.

Why dead code accumulates.

Dead code accumulates because deleting code feels risky and adding code feels safe. When an engineer refactors a module, the safe choice is to leave the old implementation in place “just in case.” When a feature is deprecated, the safe choice is to remove the UI entry point but leave the backend logic intact.

Over months and years, these safe choices compound. Each individual decision is reasonable — nobody wants to delete something that might still be needed. But the aggregate effect is a codebase where a significant fraction of the code is dead weight.

The problem is compounded by the difficulty of detection. Without a complete dependency graph, you can't confidently say whether a file is dead. It might be imported dynamically. It might be referenced by a test. It might be the entry point for a rarely-used CLI command. So teams leave it alone, and the dead code grows.

The real cost of dead code.

Dead code isn't harmless. It imposes a silent tax on every engineering activity.

Maintenance Overhead

Dead code still gets read during code reviews. Still gets considered during refactors. Still gets updated when shared interfaces change. Your team is spending cognitive cycles on code that does nothing.

Developer Confusion

New engineers can't tell which modules are live and which are abandoned. They build features on top of dead code, creating new dependencies on things that should have been deleted. The codebase becomes harder to navigate.

Expanded Security Surface

Dead code still ships. Dead dependencies still get installed. Every unused file is a file that can contain a vulnerability, and nobody is reviewing it carefully because nobody thinks it's active.

Build and Test Bloat

Dead code inflates build times, bloats bundle sizes, and distorts test coverage metrics. If 20% of your codebase is dead, your build is 20% slower than it needs to be and your coverage denominator is lying to you.

How to detect dead code.

Traditional linters catch the simplest form — unused variables and unreachable statements within a single file. But they can't detect dead files (no linter analyzes what's not imported) or orphaned database tables (that requires cross-referencing schema definitions with query patterns).

Comprehensive dead code detection requires static analysis at the system level: parsing every file with AST analysis, building a complete dependency graph, and then walking the graph to find nodes with no inbound edges. A file with zero importers and no entry-point designation is dead. A function with zero callers is dead.

Axiom Refract performs this analysis automatically across 145+ languages using Tree-sitter parsing. It identifies dead files, dead functions, orphaned database tables, and ghost methods — and reports the total recoverable lines of code so you know the exact magnitude of the cleanup opportunity.

Find out how much dead code you're carrying.

Upload your repository. Get a complete dead code inventory — files, functions, tables, ghost methods — with exact recoverable line counts.