---
title: Dead Code Detection Methodology
description: Systematic process for identifying and categorizing dead code
category: quality-standards
created: 2026-01-23
status: active
version: 1.0
---

# Dead Code Detection Methodology

## Overview

This document defines a systematic, repeatable process for identifying dead code and partially implemented functionality in the Kollabor CLI codebase.

## Purpose

- Establish clear criteria for identifying dead code vs placeholder code
- Define classification categories for different types of unused code
- Provide tools and automation for detection
- Document verification workflows
- Maintain codebase health over time

## Classification System

### Category 1: Confirmed Dead Code

**Definition:** Code that is definitively unused and can be safely removed.

**Characteristics:**
- Unreferenced functions/classes with no dynamic calls
- Unused imports with no side effects
- Unreachable code branches (after return, in dead if branches)
- Commented-out code blocks (older than 30 days)
- Duplicate definitions with one clearly unused

**Heuristics:**
- Static analysis: No references found
- Git history: No recent commits touching the code (>90 days)
- No test coverage for the code
- No plugin/dynamic import patterns

**Examples:**
```python
# Unused import
from typing import Optional  # Never used in file

# Unreferenced function
def old_legacy_function():  # No callers found
    pass

# Unreachable code
def foo():
    return "value"
    print("Never executed")  # Dead code

# Commented code older than 30 days
# def old_implementation():
#     # This was replaced 3 months ago
#     pass
```

**Action:** Safe to remove immediately (with review)

---

### Category 2: Disconnected Functionality

**Definition:** Partially implemented features that are not connected to the main codebase but may be required in the future.

**Characteristics:**
- Complete or nearly complete implementations
- No current callers but clear intended purpose
- Recent commits or active development
- Associated with TODO items or feature flags
- Has test coverage (indicating intent)

**Heuristics:**
- Static analysis: No current references
- Git history: Recent commits (<30 days)
- Has docstrings explaining purpose
- Mentioned in TODO/FIXME comments
- Has associated test files

**Examples:**
```python
# Feature not yet integrated
async def export_to_pdf(conversation: Conversation) -> bytes:
    """Export conversation to PDF format.

    TODO: Integrate with /export command when ready.
    """
    # Full implementation present
    pass

# Plugin system extension point
class AdvancedPlugin(BasePlugin):
    """Advanced plugin interface for future extension."""
    # Well-documented but no current implementers
    pass
```

**Action:** Keep with documentation, revisit quarterly

---

### Category 3: Intentional Placeholders

**Definition:** Stubs, abstract methods, and extension points that are intentionally unused.

**Characteristics:**
- Abstract base class methods
- Interface definitions
- Extension points for plugins
- NotImplementedError stubs
- Pass statements in abstract methods

**Heuristics:**
- Inherits from ABC or has @abstractmethod
- Raises NotImplementedError
- Docstring indicates "override in subclass"
- Part of plugin/extension API
- In base classes with concrete subclasses

**Examples:**
```python
from abc import ABC, abstractmethod

class BaseWidget(ABC):
    @abstractmethod
    def render(self) -> str:
        """Override in subclass to provide rendering."""
        raise NotImplementedError

# Extension point
def on_plugin_loaded(self, plugin: Plugin) -> None:
    """Hook for plugin load events. Override to customize."""
    pass
```

**Action:** Keep as-is, ensure proper documentation

---

### Category 4: Dynamic/Runtime Code

**Definition:** Code that appears unused statically but is called dynamically at runtime.

**Characteristics:**
- Called via getattr/hasattr
- Plugin discovery mechanisms
- Reflection/introspection
- Event handlers registered dynamically
- MCP server tools

**Heuristics:**
- Used with getattr, hasattr, or __dict__
- In plugin directories
- Decorated with @hook, @command, @tool
- Referenced in string constants (command names)
- Part of event bus handlers

**Examples:**
```python
# Dynamic command lookup
class CommandHandler:
    def cmd_help(self):
        """Show help."""
        pass

    def execute(self, cmd: str):
        # Dynamically calls cmd_help via getattr
        method = getattr(self, f"cmd_{cmd}", None)

# Event handler
@hook("pre_user_input")
async def on_input(event):
    """Called dynamically by event bus."""
    pass
```

**Action:** Keep, document dynamic usage pattern

---

### Category 5: Backup/Duplicate Files

**Definition:** Backup files, duplicates, and temporary files that should be removed.

**Characteristics:**
- .bak, .backup, .old extensions
- "copy" or "temp" in filename
- Exact or near-exact duplicates
- Not in .gitignore

**Heuristics:**
- File extension matching: .bak, .backup, .old, .tmp
- Filename patterns: *_copy.py, *_old.py, *~
- File similarity >95%
- Older than 7 days

**Examples:**
```bash
llm_service.py.bak
conversation_manager.py.backup
input_handler_old.py
README copy.md
```

**Action:** Delete immediately (archive if needed)

---

## Detection Tools

### 1. Vulture (Primary Tool)

**Purpose:** Detect unused Python code

**Installation:**
```bash
pip install vulture
```

**Usage:**
```bash
# Basic scan
vulture core/ plugins/ --min-confidence 80

# Detailed report with line numbers
vulture core/ plugins/ --min-confidence 60 --sort-by-size

# Ignore dynamic code patterns
vulture core/ plugins/ --ignore-names "*cmd_*,on_*,handle_*"
```

**Configuration:** `.vulture-whitelist.py`

### 2. Pylint (Secondary Tool)

**Purpose:** Detect unused imports and variables

**Usage:**
```bash
# Unused imports only
pylint core/ --disable=all --enable=unused-import

# Unused variables
pylint core/ --disable=all --enable=unused-variable,unused-argument
```

### 3. Coverage.py (Test Coverage)

**Purpose:** Identify code not executed during tests

**Usage:**
```bash
# Run tests with coverage
coverage run -m pytest tests/
coverage report --show-missing

# Generate HTML report
coverage html
```

### 4. Custom Detection Script

**Purpose:** Comprehensive scan with classification

**Location:** `scripts/detect_dead_code.py`

**Features:**
- Combines multiple tools
- Classifies findings automatically
- Generates structured reports
- Tracks findings over time

---

## Heuristics and Rules

### Confidence Levels

**High Confidence (90-100%)**
- Unused imports with no side effects
- Unreachable code after return/raise
- Backup files with .bak/.backup extensions
- Exact duplicate functions

**Medium Confidence (60-89%)**
- Functions with no direct references (may be dynamic)
- Variables assigned but never read
- Classes with no instantiations
- Methods overriding nothing

**Low Confidence (30-59%)**
- Functions in plugin directories
- Methods with @decorator (may be dynamic)
- __dunder__ methods
- Code in __init__.py files

### Pattern Matching Rules

**Ignore Patterns (Always Keep):**
```python
# Plugin system patterns
*Plugin.__init__
*Plugin.initialize
*Plugin.register_hooks
*Plugin.shutdown

# Event handlers
@hook(*)
on_*
handle_*
_handle_*

# Abstract methods
@abstractmethod
raise NotImplementedError

# Test fixtures
@pytest.fixture
conftest.py:*
```

**Flag for Review (Medium Confidence):**
```python
# Public API methods
def public_method(self):
    pass

# __all__ exports
__all__ = [...]

# setup.py entry points
entry_points = {...}
```

**Flag for Removal (High Confidence):**
```python
# Old comments
# def old_function():
#     pass

# Unreachable
return value
code_here()  # Never runs

# Obvious duplicates
def foo(): pass
def foo(): pass  # Duplicate
```

---

## Verification Workflow

### Step 1: Automated Detection

```bash
# Run detection script
python scripts/detect_dead_code.py --output reports/dead_code_$(date +%Y%m%d).json

# Review findings by category
python scripts/detect_dead_code.py --category "confirmed_dead" --format markdown
```

### Step 2: Manual Review

For each finding:

1. **Check git history:** `git log --follow <file>:<line>`
2. **Search for dynamic usage:** `grep -r "method_name" .`
3. **Check documentation:** Look for TODOs, specs mentioning it
4. **Review tests:** Does it have test coverage?
5. **Verify category:** Confirm automatic classification

### Step 3: Decision Matrix

| Category | Recent Commits (<30d) | Has Tests | Has TODO | Action |
|----------|----------------------|-----------|----------|--------|
| Confirmed Dead | No | No | No | DELETE |
| Confirmed Dead | Yes | No | No | REVIEW |
| Disconnected | Yes | Yes | Yes | KEEP + DOC |
| Disconnected | No | No | Yes | REVIEW |
| Placeholder | Any | Any | Any | KEEP |
| Dynamic | Any | Any | Any | KEEP + DOC |
| Backup | No | N/A | No | DELETE |
| Backup | Yes | N/A | No | REVIEW |

### Step 4: Documentation

For code kept as "disconnected" or "placeholder":

```python
# Add clear documentation
def future_feature():
    """Export conversation to PDF format.

    Status: NOT YET CONNECTED
    Planned for: v0.5.0
    Tracked in: #123
    Dependencies: pypdf library

    TODO: Integrate with /export command.
    """
    pass
```

### Step 5: Removal Process

```bash
# Create branch
git checkout -b cleanup/dead-code-$(date +%Y%m%d)

# Remove dead code
# ... make changes ...

# Run tests
python tests/run_tests.py

# Commit with reference
git commit -m "Remove dead code: <description>

Category: Confirmed Dead
Confidence: High
Verified: Manual review + test pass

Files affected:
- core/foo.py (removed unused_function)
- core/bar.py (removed unused import)
"

# Create PR
gh pr create --title "Dead code cleanup $(date +%Y-%m-%d)"
```

---

## Automation and Scheduling

### Weekly Automated Scan

```bash
# Cron job or CI/CD
0 0 * * 0 /path/to/detect_dead_code.py --report-only
```

### Quarterly Deep Review

**Schedule:** Every 3 months (Jan, Apr, Jul, Oct)

**Process:**
1. Run full automated scan
2. Manual review of all findings
3. Create cleanup PR
4. Update dead code analysis report
5. Archive previous report to `docs/archive/`

### Pre-Release Checklist

Before each release:
- [ ] Run dead code detection
- [ ] Remove all backup files
- [ ] Address high-confidence findings
- [ ] Update classification documentation

---

## Metrics and Reporting

### Key Metrics

**Track Over Time:**
- Total lines of code
- Number of unused functions (by confidence level)
- Number of backup files
- TODO/FIXME count
- Pass statement count
- Code health score

**Report Format:**
```
Dead Code Analysis Report - [DATE]

Code Health Score: XX/100

Findings by Category:
  [XX] Confirmed Dead (High Confidence)
  [XX] Confirmed Dead (Medium Confidence)
  [XX] Disconnected Functionality
  [XX] Backup Files

Actions Taken:
  [XX] Functions removed
  [XX] Imports cleaned
  [XX] Backup files deleted
  [XX] Files marked for future review

Cleanup Impact:
  - Disk Space: XXX KB saved
  - Lines Removed: XXX
  - Files Deleted: XX
```

---

## Integration with Development Workflow

### Pre-Commit Hook (Optional)

```bash
# .git/hooks/pre-commit
#!/bin/bash
# Warn about new backup files
if git diff --cached --name-only | grep -E '\.(bak|backup|old)$'; then
    echo "Warning: You are committing backup files"
    echo "Consider removing them before committing"
fi
```

### Code Review Checklist

When reviewing PRs:
- [ ] No new backup files added
- [ ] No commented-out code without explanation
- [ ] No unused imports
- [ ] Placeholder code properly documented
- [ ] TODO items have GitHub issue references

---

## Tool Configuration Files

### .vulture-whitelist.py

```python
"""Vulture whitelist for dynamic code patterns."""

# Plugin system
*Plugin.__init__
*Plugin.initialize
*Plugin.register_hooks
*Plugin.shutdown
*Plugin.get_status_line

# Event handlers
on_*
handle_*
_handle_*

# Command handlers
cmd_*
do_*

# Hook decorators
@hook

# Abstract methods
@abstractmethod
```

### .pylintrc (partial)

```ini
[MESSAGES CONTROL]
disable=all
enable=unused-import,
       unused-variable,
       unused-argument,
       unreachable,
       duplicate-key

[REPORTS]
output-format=json
```

---

## Best Practices

### Do:
✓ Run automated scans before each release
✓ Document disconnected functionality
✓ Use NotImplementedError for true stubs
✓ Keep git history clean (no backup files)
✓ Review low-confidence findings manually
✓ Track metrics over time

### Don't:
✗ Delete code with recent commits without review
✗ Remove code with test coverage without investigation
✗ Ignore medium-confidence findings
✗ Commit backup files to git
✗ Leave TODO items without issue references
✗ Trust 100% automated classification

---

## Examples and Case Studies

### Example 1: Confirmed Dead Code

**Finding:**
```python
# core/utils/helpers.py:145
def format_timestamp_old(ts: float) -> str:
    """Old timestamp formatter."""
    return datetime.fromtimestamp(ts).strftime("%Y-%m-%d")
```

**Analysis:**
- No references found (grep, static analysis)
- Last commit: 6 months ago
- New function `format_timestamp` exists
- No test coverage

**Decision:** DELETE (Confirmed Dead, High Confidence)

**Action:**
```bash
git checkout -b cleanup/remove-old-timestamp-formatter
# Remove function
git commit -m "Remove unused format_timestamp_old function

- No references found in codebase
- Replaced by format_timestamp in v0.3.0
- No test coverage
- Last modified 6 months ago
"
```

---

### Example 2: Disconnected Functionality

**Finding:**
```python
# core/llm/export_service.py:23
async def export_to_pdf(conversation: Conversation) -> bytes:
    """Export conversation to PDF format."""
    # 150 lines of implementation
    pass
```

**Analysis:**
- Complete implementation
- No current callers
- Recent commits (2 weeks ago)
- Has unit tests
- Referenced in TODO: "Integrate PDF export into /export command"

**Decision:** KEEP + DOCUMENT (Disconnected Functionality)

**Action:**
```python
async def export_to_pdf(conversation: Conversation) -> bytes:
    """Export conversation to PDF format.

    ⚠️ STATUS: NOT YET CONNECTED TO UI

    This feature is fully implemented and tested but not yet
    integrated into the user interface.

    Planned Integration:
    - Target: v0.5.0
    - Command: /export pdf
    - Issue: #234
    - Blocker: Awaiting pypdf version 4.0 release

    Tests: tests/unit/test_pdf_export.py
    Last Updated: 2026-01-10
    """
    # implementation
    pass
```

---

### Example 3: Dynamic Code

**Finding:**
```python
# plugins/my_plugin.py:45
async def cmd_special(self, args: str):
    """Special command handler."""
    pass
```

**Analysis:**
- No direct references
- Follows `cmd_*` pattern
- Plugin uses `getattr(self, f"cmd_{name}")`
- Has test coverage via command execution

**Decision:** KEEP (Dynamic/Runtime Code)

**Action:** No changes needed, pattern is documented

---

## Appendix A: Tool Comparison

| Tool | Strengths | Weaknesses | Best For |
|------|-----------|------------|----------|
| **Vulture** | Fast, accurate, customizable | False positives on dynamic code | Primary detection |
| **Pylint** | Comprehensive checks | Slow on large codebases | Import cleanup |
| **Coverage.py** | Shows what's tested | Requires running tests | Finding untested code |
| **mypy** | Type-aware analysis | Requires type hints | Type-based unused detection |
| **Custom Script** | Project-specific rules | Maintenance overhead | Classification |

---

## Appendix B: Change Log

| Version | Date | Changes |
|---------|------|---------|
| 1.0 | 2026-01-23 | Initial methodology document |

---

## References

- Previous Analysis: `docs/archive/dead_code_analysis_report.md`
- Current Report: `docs/archive/deadcode.md`
- Python Standards: `docs/standards/coding/python-coding-standards.md`
- Code Review SOP: `docs/sop/development/code-review-process.md`
