# How to Find Dead Code Using code-graph-rag: Graph-Based Detection Guide

> Discover how to find dead code with code-graph-rag. This guide explains using GraphQueryClient and collect_dead_code() for efficient detection of unreachable symbols. Optimize your codebase today.

- Repository: [Vitali Avagyan/code-graph-rag](https://github.com/vitali87/code-graph-rag)
- Tags: how-to-guide
- Published: 2026-09-05

---

**You can find dead code using code-graph-rag by initializing a `GraphQueryClient`, configuring analysis settings with `default_dead_code_config()`, and executing `collect_dead_code()` to identify symbols unreachable from defined root entry points.**

Finding dead code in large, multi-language codebases requires tracing call relationships across language boundaries and framework patterns. The `code-graph-rag` repository provides a dedicated reachability engine in [`codebase_rag/dead_code.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/dead_code.py) that performs graph-based analysis to detect unused symbols. This guide explains how to leverage this engine to find dead code across Python, Rust, Java, C++, and other supported languages.

## How the Dead Code Detection Engine Works

The dead code engine operates by building a reachability graph from **CALLS** and **REFERENCES** edges stored in the graph database. According to the implementation in [`codebase_rag/dead_code.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/dead_code.py), the analysis follows a three-phase process:

- **Root Identification** (lines 15-94): The engine scans candidate nodes to identify **roots**—entry points like framework hooks, exported symbols, test decorations, and language-specific runtime symbols. Helper functions like `_is_dunder`, `_is_rust_runtime_root`, and `_has_root_decorator` handle patterns across Python, Rust, Java, C#, JavaScript/TypeScript, and NestJS.

- **Graph Traversal** (lines 75-89): A breadth-first search walks the adjacency map starting from identified roots, marking all reachable symbols as live code.

- **Factory and Override Expansion** (lines 133-170): After the initial walk, the engine performs fixed-point iteration over override relationships and factory-class mappings to ensure polymorphic calls and dependency injection roots are properly traced.

Any symbol not visited during this process is reported as dead code, with results filtered through user-defined exclusion patterns.

## Step-by-Step: Finding Dead Code with the Python API

To programmatically find dead code using code-graph-rag, instantiate a query client and invoke the analysis pipeline.

### Initialize the Graph Query Client

First, create a `GraphQueryClient` connected to your graph database (Memgraph or the in-memory test harness):

```python
from codebase_rag.graph_query import GraphQueryClient

client = GraphQueryClient(
    uri="bolt://localhost:7687",
    user="neo4j",
    password="*****"
)

```

### Configure Root Detection and Exclusions

Use `default_dead_code_config()` (defined at lines 41-53 in [`codebase_rag/dead_code.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/dead_code.py)) to create a `DeadCodeConfig` object that controls whether test code and class symbols are treated as roots, and to specify exclusion patterns:

```python
from codebase_rag.dead_code import default_dead_code_config

cfg = default_dead_code_config(
    include_tests=False,
    include_classes=False,
    exclude_patterns=(
        "**/node_modules/**",
        "**/generated/**",
    )
)

```

### Execute the Analysis

Call `collect_dead_code()` (public API at lines 61-69) to retrieve unreachable symbols. The function returns rows containing the fully-qualified name and source path for each dead symbol:

```python
from codebase_rag.dead_code import collect_dead_code

dead_rows = collect_dead_code(
    client=client,
    project_name="myproj",
    config=cfg
)

for row in dead_rows:
    print(f"{row['qualified_name']}  →  {row['path']}")

```

The `cgr_dead_code()` wrapper in [`evals/dead_code.py`](https://github.com/vitali87/code-graph-rag/blob/main/evals/dead_code.py) provides a convenience interface used by the CLI, but `collect_dead_code()` exposes the full API for custom integrations.

## Running Dead Code Analysis from the Command Line

For CI/CD integration or quick checks, use the `cgr` CLI entry point defined in [`codebase_rag/graph_cli.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/graph_cli.py). The `dead-code` subcommand wraps the same Python API:

```bash
cgr dead-code \
    --target /path/to/my/project \
    --project-name myproj \
    --exclude-patterns "**/generated/**" \
    --include-tests false \
    --include-classes false

```

The CLI handles argument parsing, constructs the `DeadCodeConfig`, and prints a formatted table of dead symbols identified by the reachability engine.

## Customizing Root Detection for Framework-Specific Patterns

The engine recognizes language-specific roots without manual configuration. In [`codebase_rag/dead_code.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/dead_code.py), the detection logic handles:

- **Python**: Dunder methods and test decorators
- **Rust**: Runtime roots and test attributes
- **Java**: Serialization hooks and entry functions
- **C/C++**: Main entry points and library exports
- **C#**: Attributes and framework hooks
- **JavaScript/TypeScript**: NestJS DI roots and React component lifecycle methods

You can extend this behavior by modifying the `_is_*_root` helper functions in the source, or by using `exclude_patterns` in the configuration to filter generated or vendored code that should not be analyzed for liveness.

## Summary

- **Find dead code using code-graph-rag** by querying unreachable nodes from a graph of CALLS and REFERENCES edges.
- The `collect_dead_code()` function in [`codebase_rag/dead_code.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/dead_code.py) implements a breadth-first search from identified roots, handling polymorphism through override and factory-class expansion.
- Configure analysis with `default_dead_code_config()` to control test inclusion, class symbol handling, and exclusion patterns.
- Use the CLI command `cgr dead-code` for command-line integration or the Python API for programmatic analysis.
- The engine supports Python, Rust, Java, C++, C#, and JavaScript/TypeScript through language-specific root detection helpers.

## Frequently Asked Questions

### What constitutes a "root" in dead code detection?

A root is any symbol that serves as a legitimate entry point for execution or external reference. According to the implementation in [`codebase_rag/dead_code.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/dead_code.py) (lines 15-94), roots include framework hooks like React lifecycle methods, NestJS DI providers, exported library symbols, test functions decorated with test attributes, and language-specific runtime entry points like `main` functions or Python dunders.

### How does code-graph-rag handle inheritance and factory patterns?

After the initial graph walk, the engine performs additional traversal phases (lines 133-170 in [`codebase_rag/dead_code.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/dead_code.py)) that specifically handle override relationships and factory-class mappings. This ensures that if a base class method is called through polymorphism, or if a factory instantiates objects via dependency injection, those symbols are marked as reachable even if not directly invoked in static analysis.

### Can I include test files in the dead code analysis?

Yes. The `include_tests` parameter in `default_dead_code_config()` controls whether test-related decorations and test files are considered roots. Setting `include_tests=True` treats test entry points as live code, which prevents test-only utilities from being flagged as dead. Set it to `False` when you want to identify dead code within the test suite itself.

### Which graph databases are supported for dead code analysis?

The `GraphQueryClient` used by `collect_dead_code()` supports **Memgraph** via Bolt protocol and an in-memory test harness used for evaluation. The URI connection string in the client initialization determines the backend, allowing integration with existing Memgraph deployments or lightweight local analysis without external database dependencies.