How code-review-graph Performs Blast-Radius Analysis for Code Changes
Blast-radius analysis in code-review-graph identifies all potentially affected code entities by traversing the program graph from modified files using a SQLite-based best-score relaxation algorithm that applies depth-decay multipliers to rank impact severity.
The code-review-graph open-source tool helps developers understand the potential impact of code changes before they merge. Its blast-radius analysis feature calculates how far a modification ripples through the codebase by walking relationships between functions, classes, and their dependents. This analysis lives primarily in code_review_graph/graph.py and exposes both a Python API and CLI for integration into review workflows.
How the Blast-Radius Algorithm Works
The core logic resides in Graph.get_impact_radius within code_review_graph/graph.py. The system accepts a list of changed file paths and returns every logical code entity that could be affected, weighted by a calculated impact score.
Entry Point and Engine Dispatch
Analysis begins when the CLI command crg impact … or the Python tool get_impact_radius_tool forwards changed file paths to store.get_impact_radius. According to the source in code_review_graph/main.py around line 220, the implementation routes to Graph.get_impact_radius, which selects between two engines:
- SQLite engine (default): Uses temporary tables and SQL-based traversal for high performance
- NetworkX engine (legacy): Activated by setting the environment variable
CRG_BFS_ENGINE=networkx
The dispatch logic at line 1317 in code_review_graph/graph.py automatically chooses the fast SQLite path unless explicitly overridden.
Seed Preparation and Initialization
Before traversal begins, the system identifies seed nodes representing the changed files. The algorithm creates a temporary table named _impact_seeds populated with the qualified names of modified nodes, each assigned an initial score of 1.0. This initialization occurs between lines 1392 and 1402 in code_review_graph/graph.py, establishing the starting frontier for the graph walk.
Best-Score Relaxation in SQL
The SQLite implementation uses an iterative best-score relaxation technique expressed through temporary tables _impact_frontier, _impact_next, and _impact_best. For each iteration:
- The algorithm expands the current frontier to neighboring nodes
- It multiplies the current score by the edge weight (retrieved from the
extracolumn or defaulted to1.0) and theIMPACT_DEPTH_DECAYfactor (approximately0.5) - It keeps only the highest score per node, discarding lower-scoring paths to the same entity
This logic appears between lines 1439 and 1464 in code_review_graph/graph.py. The depth-decay mechanism ensures that transitive dependencies receive exponentially lower impact scores than direct callers.
Termination Criteria and Results
Expansion continues until the frontier becomes empty or all scores fall below IMPACT_SCORE_FLOOR. The termination check and final result assembly happen between lines 1464 and 1556. During assembly:
- Nodes are sorted by descending impact score
- File-type nodes are intentionally omitted, returning only logical code entities (functions, classes, etc.)
- The method returns both the list of impacted nodes and a mapping of
impact_scores[qualified_name] → score
NetworkX Fallback Implementation
When CRG_BFS_ENGINE=networkx is set, the system falls back to a straightforward breadth-first search on a NetworkX DiGraph built from the same edge table. This implementation, found between lines 1564 and 1580 in code_review_graph/graph.py, functionally mirrors the SQL approach but executes much slower due to Python-level traversal overhead.
Configuring Impact Scoring
The blast-radius calculation exposes several parameters to balance precision against performance:
IMPACT_DEPTH_DECAY– Each hop away from the modified code multiplies the score by approximately0.5, ensuring distant dependencies receive lower priorityIMPACT_SCORE_FLOOR– The minimum score threshold for continued traversal; nodes scoring below this value terminate expansion along that pathmax_depth– Limits the traversal depth (CLI flag--depthor environment variableCRG_MAX_IMPACT_DEPTH). The default value of2captures direct callers and immediate dependents, covering over 90% of real-world regressions while maintaining fast query execution
Edge weights are extracted from the extra column of the edge table when available, defaulting to 1.0 for unweighted relationships.
Usage Examples
Querying Blast Radius via Python API
from code_review_graph.main import GraphStore
store = GraphStore(repo_path="/my/project")
changed = ["/my/project/src/auth.py"]
# Analyze with default depth of 2
impact = store.get_impact_radius(changed, max_depth=2)
print("Impacted nodes:")
for node in impact["impacted_nodes"]:
score = impact["impact_scores"][node.qualified_name]
print(f"- {node.kind}: {node.qualified_name} (score={score:.2f})")
Command Line Interface
# Analyze specific files with custom depth
$ crg impact src/auth.py --depth 3
Wide blast radius: 27 impacted symbols
1. function login (score 1.00)
2. function check_credentials (score 0.50)
3. function validate_token (score 0.25)
Integration in Review Workflows
from code_review_graph.tools.review import review_pr
# Automatically add blast-radius warnings to PR reviews
summary = review_pr(
repo_path="/my/project",
pr_number=42,
max_depth=2,
)
# summary contains: "Wide blast radius: 27 impacted nodes"
print(summary)
Summary
- Blast-radius analysis walks the program graph from modified files to identify affected logical entities using
Graph.get_impact_radiusincode_review_graph/graph.py - The default SQLite engine uses temporary tables (
_impact_seeds,_impact_frontier,_impact_best) and iterative best-score relaxation with depth decay - NetworkX fallback provides a slower but equivalent BFS implementation when
CRG_BFS_ENGINE=networkxis set - Impact scores decay exponentially via
IMPACT_DEPTH_DECAY(≈0.5 per hop) and terminate belowIMPACT_SCORE_FLOOR - File nodes are excluded from final results, returning only logical code symbols like functions and classes
- The API supports
max_depthconfiguration (default 2) via theget_impact_radiusparameter or--depthCLI flag
Frequently Asked Questions
How does the algorithm handle circular dependencies?
The best-score relaxation mechanism naturally handles cycles by keeping only the highest score per node in the _impact_best temporary table. If a cycle is encountered, subsequent visits to an already-processed node with lower scores are discarded, preventing infinite loops while preserving the strongest impact path through the cycle.
What is the performance difference between the SQLite and NetworkX engines?
The SQLite engine performs traversal entirely within the database engine using set-based SQL operations, making it significantly faster for large codebases. The NetworkX fallback builds a DiGraph in memory and executes Python-level BFS, which incurs substantial overhead according to the implementation in code_review_graph/graph.py lines 1564-1580. The SQLite path is preferred for production use.
Can I adjust how quickly the impact score decays with depth?
While the internal IMPACT_DEPTH_DECAY is hardcoded to approximately 0.5, you can effectively control sensitivity through the max_depth parameter. Increase --depth via CLI or max_depth via the Python API to analyze deeper transitive dependencies. Limiting depth to the default of 2 captures direct callers and immediate dependents while keeping queries fast.
Why are file nodes excluded from the blast-radius results?
The algorithm intentionally filters out file-type nodes during result assembly in code_review_graph/graph.py (lines 1529-1556) because logical code entities (functions, classes, methods) provide more actionable review feedback than entire files. This filtering ensures reviewers focus on specific callable units rather than being overwhelmed by broad file-level notifications.
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 →