| name | dead-code-eliminator |
| description | Identify and analyze unused or redundant code including unused functions/methods, unused variables/imports, unreachable code, and redundant conditions. Use when cleaning up codebases, improving maintainability, reducing technical debt, or conducting code quality audits. Analyzes Python code using AST analysis and produces markdown reports listing dead code locations with line numbers, severity ratings, and recommendations. Triggers when users ask to find dead code, remove unused code, identify unused imports, find unreachable code, or clean up redundant logic. |
Dead Code Eliminator
Overview
Systematically identify unused or redundant code in Python codebases to improve maintainability, reduce confusion, and eliminate technical debt.
Workflow
1. Understand the Scope
Define what to analyze:
Questions to ask:
- What directory or files should be analyzed?
- Should test files be included or excluded?
- Are there specific types of dead code to focus on?
- Should external-facing API functions be considered?
Determine analysis scope:
ls -la
find . -name "*.py" | wc -l
find . -type d -name "*test*"
2. Identify Dead Code
Use multiple detection strategies to find different types of dead code.
Strategy 1: Find Unused Functions
Use the bundled script for AST-based analysis:
python scripts/find_unused_functions.py /path/to/project
python scripts/find_unused_functions.py /path/to/project tests,venv,docs
What it detects:
- Functions defined but never called
- Methods that aren't invoked anywhere
- Async functions without callers
Limitations:
- Won't detect dynamically called functions (via getattr, decorators)
- May flag public API functions that are used externally
- Doesn't detect pytest fixtures or entry points
Strategy 2: Find Unused Imports
Use the bundled script to identify unused imports:
python scripts/find_unused_imports.py /path/to/file.py
python scripts/find_unused_imports.py /path/to/project
python scripts/find_unused_imports.py /path/to/project venv,.venv,tests
What it detects:
- Imports that are never referenced
- Unused
from X import Y statements
- Redundant imports
Strategy 3: Use External Tools
Leverage Python ecosystem tools for comprehensive analysis:
vulture - Finds unused code:
pip install vulture
vulture /path/to/project
vulture /path/to/project --exclude venv,tests
vulture /path/to/project --min-confidence 80
autoflake - Focuses on imports and variables:
pip install autoflake
autoflake --check --imports /path/to/file.py
autoflake --check --remove-all-unused-imports --remove-unused-variables /path/to/file.py
autoflake --check -r /path/to/project
pylint - General linting including dead code:
pip install pylint
pylint /path/to/project --disable=all --enable=unused-import,unused-variable,unreachable
Strategy 4: Manual Code Review
Read the code to identify patterns:
Unreachable code:
- Code after
return statements
- Code in impossible conditions
- Code after
break, continue, or raise
Redundant conditions:
- Always-true or always-false checks
- Duplicate conditions
- Unnecessary
else after return
Look for:
grep -A 3 "return" **/*.py | grep -v "^--$"
grep -r "def.*old\|def.*legacy" .
grep -r "TODO.*remove\|FIXME.*delete" .
3. Categorize Findings
Organize dead code by type and priority.
See dead-code-patterns.md for comprehensive pattern catalog.
Category: Unused Imports
Priority: High (easy to remove, low risk)
Examples:
import os but os is never used
from typing import List, Dict but only List is used
- Duplicate imports
Category: Unused Functions/Methods
Priority: Medium to High
Subcategories:
- Orphaned helpers: Utility functions never called
- Refactoring leftovers: Old implementations not removed
- Test helpers: Test utilities not used by any test
Caution - May be intentional:
- Public API functions (used externally)
- Plugin/hook functions (called dynamically)
- CLI entry points (called from command line)
Category: Unreachable Code
Priority: High (indicates bugs or confusion)
Examples:
- Code after
return
- Code in impossible conditions
- Code after
raise
Category: Redundant Code
Priority: Medium
Examples:
- Redundant boolean checks
- Unnecessary
else after return
- Duplicate logic in multiple places
Category: Unused Variables
Priority: Low to Medium
Examples:
- Assigned but never read
- Function parameters never used
- Loop variables never referenced
4. Verify Findings
Before reporting, verify that identified code is truly dead.
Check for dynamic usage:
handlers = {
'process': process_handler,
}
handler = getattr(module, function_name)
Check for external usage:
- Is this a public API function?
- Is it documented in README or API docs?
- Is it an entry point in setup.py?
Check for framework conventions:
@receiver(post_save, sender=User)
def user_saved(sender, instance, **kwargs):
pass
@pytest.fixture
def sample_data():
return {"key": "value"}
Verify with grep:
grep -r "function_name" .
grep -r "'function_name'\|\"function_name\"" .
grep -r "function_name" setup.py pyproject.toml
5. Generate Report
Create a structured markdown report of findings.
Dead Code Analysis Report
Project: [Project Name]
Analyzed: [Date]
Scope: [Directories analyzed]
Excluded: [Excluded directories]
Summary
- Unused imports: X findings across Y files
- Unused functions: A findings across B files
- Unreachable code: M findings
- Redundant code: N findings
Total: Z dead code instances found
🔴 High Priority Issues
Unreachable Code
Code that can never execute - should be removed immediately.
Issue 1: Code After Return
Location: src/utils.py:45-47
Code:
def process_data(data):
if not data:
return None
logging.warning("Empty data")
validate(data)
Recommendation: Remove lines 46-47 (unreachable after return).
Impact: Misleading code that suggests validation happens but doesn't.
Unused Imports
File: src/main.py
Lines:
- Line 3:
import os (unused)
- Line 5:
from typing import Dict, Tuple (only Dict is used)
- Line 12:
import re (unused)
Recommendation: Remove unused imports. Update line 5 to from typing import Dict.
🟡 Medium Priority Issues
Unused Functions
Functions that appear unused but should be verified before removal.
Issue 3: Orphaned Helper Function
Location: src/helpers.py:89
Function: format_timestamp(ts: int) -> str
Analysis:
- Defined but never called in codebase
- Not in public API documentation
- Not an entry point
Verification needed:
- ✅ Checked: Not in setup.py entry_points
- ✅ Checked: Not mentioned in README
- ✅ Checked: No string references in codebase
- ❌ Need to verify: Could this be used by external code?
Recommendation: If not part of public API, remove. Otherwise, document it.
Issue 4: Duplicate Logic
Locations:
src/processor_a.py:45-52
src/processor_b.py:78-85
Code: Both files contain identical validation logic.
Recommendation: Extract common logic into shared utility function.
Impact: Maintenance burden - changes must be duplicated.
🔵 Low Priority Issues
Redundant Code
Issue 5: Unnecessary Else After Return
Location: src/validator.py:123-127
Code:
def check_status(value):
if value > 0:
return "positive"
else:
return "negative"
Recommendation: Remove else clause (implicit after return).
Impact: Minor - slightly less readable but no functional impact.
Issue 6: Unused Variable
Location: src/calculator.py:56
Code:
def compute(a, b):
total = a + b
return a * b
Recommendation: Remove unused total variable.
⚪ Needs Verification
Potentially Used Dynamically
These appear unused but may be called dynamically. Manual verification needed.
Function: handle_create()
Location: src/handlers.py:34
Reason for caution: File contains handler registry suggesting dynamic dispatch.
Code pattern:
HANDLERS = {
'create': handle_create,
'update': handle_update,
}
Recommendation: Verify this is registered and used. If confirmed unused, remove.
Recommendations
Immediate Actions (High Priority)
- Remove unreachable code (Issue 1)
- Clean up unused imports across all files
- Verify and remove orphaned helper functions
Short-term Actions (Medium Priority)
- Extract duplicate logic into shared utilities
- Verify dynamically-called functions
- Remove confirmed unused functions
Long-term Actions (Low Priority)
- Simplify redundant conditions
- Remove unused variables
- Establish linting rules to prevent future dead code
Prevention
Add to CI/CD pipeline:
vulture src/ --min-confidence 80
autoflake --check -r src/
Configure IDE:
- Enable unused import warnings
- Configure pylint/flake8 for dead code detection
Code review checklist:
Detailed Findings
[If needed, include full lists of all findings organized by file]
6. Present Findings
Share the report with the team and get feedback.
Present clearly:
- Start with summary statistics
- Highlight high-priority issues first
- Provide specific locations and recommendations
- Separate definite dead code from "needs verification"
Be cautious about:
- Public API functions that may be used externally
- Dynamic dispatch patterns
- Framework-specific code (decorators, fixtures, signals)
- CLI entry points
- Plugin systems
Request feedback:
- "Are these functions part of the public API?"
- "Is this code used by external tools or scripts?"
- "Should we keep this for planned features?"
Tips for Effective Dead Code Analysis
Start conservatively:
- Focus on obvious cases first (unused imports, unreachable code)
- Be cautious with unused functions (may be called externally)
- Verify before removing
Use multiple detection methods:
- AST-based analysis (bundled scripts)
- External tools (vulture, autoflake, pylint)
- Manual code review
- Coverage analysis (find untested code)
Prioritize by risk:
- Low risk: Unused imports, unused variables
- Medium risk: Unused internal functions, redundant code
- Higher risk: Functions that might be public API
Consider the context:
- Age of code (old = more likely truly dead)
- Recent refactoring (may be leftovers)
- Project type (library vs application)
- Team size (more people = more likely to have external usage)
Prevention is better than cure:
- Enable linting in CI/CD
- Configure IDE warnings
- Code review checklist
- Regular dead code audits
Common False Positives
Be aware of code that appears dead but isn't:
1. Dynamic dispatch:
handler = getattr(module, f"handle_{action}")
2. Entry points:
entry_points={
'console_scripts': ['tool=module:main_function']
}
3. Pytest fixtures:
@pytest.fixture
def sample_data():
return data
4. Django signals:
@receiver(post_save, sender=Model)
def handle_save(sender, instance, **kwargs):
pass
5. Decorators and metaclasses:
class Meta:
def __init_subclass__(cls):
register(cls)
6. Public API functions:
def public_function():
pass
Reference
For comprehensive dead code patterns and detection strategies, see dead-code-patterns.md.