How to Use Refactoring Tools for Renaming Symbols and Detecting Dead Code in code-review-graph
The code-review-graph package provides a thread-safe refactoring engine that lets you preview and apply symbol renames across your codebase, detect unused functions and classes, and generate automated removal suggestions.
The code_review_graph.refactor module exposes a production-ready API for automating large-scale code transformations without breaking your repository. These tools operate on a GraphStore instance—the in-memory code graph built by the parser—to ensure every rename and deletion suggestion accounts for cross-file dependencies, import relationships, and test coverage.
Setting Up the GraphStore
All refactoring operations require an initialized GraphStore containing the parsed representation of your codebase. This object serves as the single source of truth for symbol locations, call sites, and import graphs.
from code_review_graph.graph import GraphStore
from pathlib import Path
# Load an existing graph database populated by the parser
store = GraphStore("path/to/graph.db")
Renaming Symbols Safely
The renaming workflow follows a preview-then-apply pattern that prevents accidental breaking changes. Each preview receives a unique refactor_id and remains available for approximately ten minutes (REFACTOR_EXPIRY_SECONDS), allowing you to review diffs before committing changes.
Previewing a Rename Operation
Call rename_preview() to analyze the impact of changing a symbol name without modifying any files. This function looks up the node by its current name, gathers all definition sites, call sites, and import references, then stores the edit plan in a thread-safe _pending_refactors dictionary.
from code_review_graph.refactor import rename_preview
preview = rename_preview(
store=store,
old_name="legacy_helper",
new_name="new_helper"
)
if preview is None:
raise ValueError("Symbol not found in graph")
refactor_id = preview["refactor_id"]
print(f"Preview {refactor_id}: {len(preview['edits'])} files will be modified")
According to the source code in code_review_graph/refactor.py (lines 73-82), the preview mechanism uses _refactor_lock to ensure thread safety when multiple processes access the pending refactor store simultaneously.
Reviewing Changes with Dry-Run
Before applying changes to disk, use apply_refactor() with dry_run=True to generate a unified diff. This retrieves the stored preview by refactor_id, validates that it has not expired, and produces a diff without writing to the filesystem. The preview remains available after a dry-run, allowing you to apply the same refactor later.
from code_review_graph.refactor import apply_refactor
dry_result = apply_refactor(
refactor_id=refactor_id,
repo_root=Path("/path/to/repo"),
dry_run=True
)
# Display unified diffs for each affected file
for file_path, diff in dry_result["diffs"].items():
print(f"--- {file_path} ---")
print(diff)
The diff generation logic resides at lines 334-447 in code_review_graph/refactor.py, producing standard unified diff output suitable for code review tools.
Applying the Refactor
Once you verify the diff, execute the rename by calling apply_refactor() without the dry-run flag. The function validates that every edit path falls within repo_root to prevent path-traversal attacks, writes the new content to affected files, and removes the preview from _pending_refactors.
result = apply_refactor(
refactor_id=refactor_id,
repo_root=Path("/path/to/repo")
)
print(f"Applied {result['edits_applied']} edits across {len(result['files_modified'])} files")
The core application logic, including path validation and filesystem safety checks, is implemented at lines 114-150 in code_review_graph/refactor.py.
Detecting and Removing Dead Code
The refactoring engine includes static analysis capabilities to identify symbols with zero references, helping you maintain a clean codebase.
Finding Unused Symbols
The find_dead_code() function scans the GraphStore for symbols meeting five criteria: no callers, no test references, no importers, no value references, and not recognized as an entry point. It automatically excludes framework-managed classes, dunder methods, constructors, Angular lifecycle hooks, and mock variables.
from code_review_graph.refactor import find_dead_code
dead_symbols = find_dead_code(
store=store,
kind="function", # Optional: filter by "function", "class", etc.
file_pattern="*.py" # Optional: restrict to specific file patterns
)
for symbol in dead_symbols:
print(f"Dead {symbol['kind']} '{symbol['name']}' at {symbol['file_path']}:{symbol['line']}")
The dead-code detection algorithm begins at line 244 in code_review_graph/refactor.py, utilizing helper functions such as _is_entry_point(), _is_test_file(), and _collect_type_referenced_names() to filter false positives.
Generating Removal Suggestions
For automated cleanup workflows, suggest_refactorings() converts dead symbols into structured removal recommendations. When community detection data exists, it also suggests moving functions that reside in one module but are exclusively called from another.
from code_review_graph.refactor import suggest_refactorings
suggestions = suggest_refactorings(store)
for suggestion in suggestions:
if suggestion["type"] == "remove":
print(f"{suggestion['description']}")
print(f"Rationale: {suggestion['rationale']}")
This functionality is implemented at lines 216-260 in code_review_graph/refactor.py, returning dictionaries containing type, description, symbols, and rationale fields.
Safety Mechanisms and Thread Safety
The refactoring tools implement multiple safeguards to protect your repository:
- Thread-safe operations: All access to
_pending_refactorsuses_refactor_lockto prevent race conditions during concurrent preview and apply operations. - Expiry protection: Previews automatically expire after approximately ten minutes (
REFACTOR_EXPIRY_SECONDS), preventing stale refactors from being applied accidentally. - Filesystem containment: The
apply_refactor()function validates that every target path resolves within the specifiedrepo_root, blocking path-traversal attacks that attempt to write outside the repository.
Summary
- Initialize a
GraphStorefrom your parsed code graph to enable refactoring operations. - Preview renames using
rename_preview(), which stores an edit plan in a thread-safe, expiring cache. - Review diffs safely with
apply_refactor(..., dry_run=True)before committing changes. - Apply changes via
apply_refactor()with path-traversal protection and automatic cleanup of pending previews. - Detect dead code with
find_dead_code(), which filters out tests, entry points, and framework constructs. - Generate suggestions using
suggest_refactorings()to receive structured removal and relocation recommendations.
Frequently Asked Questions
How does code-review-graph prevent accidental file modifications outside the repository?
The apply_refactor() function in code_review_graph/refactor.py (lines 114-150) validates every edit path using filesystem checks to ensure it resolves within the specified repo_root. If any edit targets a path outside this boundary, the operation returns an error status without writing to the filesystem, preventing path-traversal vulnerabilities.
Can multiple developers run refactoring operations simultaneously on the same server?
Yes. The module uses a _refactor_lock (threading.Lock) to synchronize access to the _pending_refactors dictionary, ensuring that preview creation and application operations remain atomic. Each preview receives a unique refactor_id, isolating concurrent refactoring sessions.
What types of symbols does the dead code detector ignore?
The find_dead_code() function excludes symbols recognized as entry points, framework-managed classes (like Angular components), dunder methods, constructors, mock variables, and any symbols referenced by test files. These exclusions are handled by helper functions _is_entry_point() and _is_test_file() starting at line 244 in code_review_graph/refactor.py.
How long do rename previews remain available for application?
Previews expire after approximately ten minutes (REFACTOR_EXPIRY_SECONDS). Attempting to apply a refactor after expiry returns an error, requiring you to regenerate the preview. This timeout is implemented in the storage logic at lines 73-82 of code_review_graph/refactor.py.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →