# How detect_changes Computes Risk Scores for Code Modifications

> Learn how detect_changes computes risk scores for code modifications by analyzing flow participation, community crossing, test coverage, and security sensitivity. Get a normalized risk value.

- Repository: [Tirth Kanani/code-review-graph](https://github.com/tirth8205/code-review-graph)
- Tags: how-to-guide
- Published: 2026-07-25

---

**The `detect_changes` module in `tirth8205/code-review-graph` calculates risk scores by aggregating six weighted factors—including flow participation, community crossing, test coverage, and security sensitivity—into a normalized 0.0 to 1.0 value using the `compute_risk_score` function.**

The `detect_changes` functionality within the `tirth8205/code-review-graph` repository provides automated risk assessment for code reviews by analyzing graph-based relationships between modified functions and their architectural context. When a pull request modifies specific files, the system maps these changes to graph nodes and evaluates how dangerous each modification might be to system stability. This risk scoring mechanism enables reviewers to prioritize their attention on the most critical changes first.

## The Core Risk Calculation Logic

The risk scoring implementation resides in [`code_review_graph/changes.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/changes.py), specifically within the `compute_risk_score` function (approximately lines 30–74). This function aggregates multiple architectural and semantic signals, each contributing a capped weighted value to the final score.

### Flow Participation Factor

The algorithm first examines whether the modified node participates in critical system flows. It retrieves flow criticalities via `store.get_flow_criticalities_for_node`:

- If criticality values exist, their sum is added (capped at **0.25**)
- If no criticalities are defined, the count of flow memberships is multiplied by **0.05** (also capped at **0.25**)

This ensures that nodes touching multiple critical paths receive higher risk weighting without exceeding the quarter-point threshold.

### Community Crossing Impact

Crossing architectural boundaries increases risk. The function counts callers belonging to different communities than the target node using `store.get_node_community_id` and `store.get_community_ids_by_qualified_names`:

- Each community crossing adds **0.05** to the score
- Maximum contribution capped at **0.15**

This penalizes changes that act as integration points between distinct system modules.

### Test Coverage Penalty

The system evaluates testing safety nets via `store.get_transitive_tests`:

- **0 tests**: Contributes **0.30** (high risk)
- Each test reduces penalty by **0.05**
- **5+ tests**: Contributes **0.05** (minimum risk floor)

This inverse relationship ensures untested code receives significantly higher risk scores than well-tested components.

### Security Sensitivity Check

The function scans for security-critical identifiers:

- Checks if `node.name` or `node.qualified_name` contains keywords from `SECURITY_KEYWORDS`
- Match adds **0.20** to the total score

This static analysis layer catches modifications to encryption, authentication, or authorization routines regardless of graph topology.

### Caller Count Weight

Dependency popularity influences risk through `store.get_edges_by_target`:

- Contribution equals `min(caller_count / 20, 0.10)`
- Maximum contribution: **0.10**

Widely called utilities receive higher scores than isolated internal functions.

### Optional Change Frequency (Churn)

When `include_churn=True`, the algorithm incorporates historical modification data:

- Looks up commit counts touching the node's file via `churn_counts`
- Formula: `min(commit_count / _CHURN_SATURATION, 1.0) * _CHURN_WEIGHT`
- Maximum contribution approximately **0.15**

Frequently modified files indicate unstable code requiring additional scrutiny.

## Score Normalization and Output

After summing all factors, `compute_risk_score` clamps and formats the result:

```python
return round(min(max(score, 0.0), 1.0), 4)

```

This guarantees all scores fall within the **0.0 – 1.0** range with four-decimal precision, enabling consistent comparison across different node types and project sizes.

## Practical Usage Examples

Calculate risk for a single modified function:

```python
from code_review_graph.changes import compute_risk_score
from code_review_graph.graph import GraphStore

store = GraphStore.load("/path/to/graph")
node = store.get_node_by_qualified_name("myproject.utils.encrypt_data")

risk = compute_risk_score(store, node)
print(f"Risk score: {risk}")  # Output: 0.73

```

Execute full PR analysis with churn data:

```python
from code_review_graph.changes import analyze_changes

result = analyze_changes(
    store,
    changed_files=["src/crypto.py", "src/auth.py"],
    repo_root="/path/to/repo",
    include_churn=True
)

for item in result["review_priorities"]:
    print(f"{item['qualified_name']}: {item['risk_score']}")

```

## Integration with the Analysis Pipeline

The `analyze_changes` function orchestrates the complete workflow defined in [`code_review_graph/changes.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/changes.py). It:

1. Parses diffs to identify changed nodes
2. Invokes `compute_risk_score` for each modified function/class
3. Complements scores with flow-impact data from [`code_review_graph/flows.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/flows.py) via `get_affected_flows`
4. Returns the top-10 highest-risk nodes as review priorities

Supporting infrastructure in [`code_review_graph/graph.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/graph.py) provides the `GraphStore` APIs—`get_flow_criticalities_for_node`, `get_transitive_tests`, and `get_edges_by_target`—that supply the raw metrics feeding into the risk algorithm.

## Summary

- **Six weighted factors** determine risk: flow participation (max 0.25), community crossing (max 0.15), test coverage (0.05–0.30), security sensitivity (0.20), caller count (max 0.10), and optional churn (max ~0.15)
- **Normalization** ensures scores always fall between 0.0 and 1.0 with four-decimal precision
- **Primary implementation** lives in [`code_review_graph/changes.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/changes.py) within the `compute_risk_score` function
- **Integration** occurs through `analyze_changes`, which processes PR diffs and returns prioritized review queues
- **Dependencies** on `GraphStore` APIs provide architectural context for community detection, flow membership, and test coverage analysis

## Frequently Asked Questions

### What is the maximum possible risk score detect_changes can assign?

The maximum risk score is **1.0**. While the sum of all weighted factors could theoretically exceed this value, the `compute_risk_score` function explicitly clamps the result using `min(max(score, 0.0), 1.0)` before returning. This normalization ensures consistent scoring boundaries across different codebases and change types.

### How does detect_changes penalize code with no test coverage?

Nodes with zero transitive test dependencies receive a **0.30** penalty—the largest single factor in the scoring algorithm. Each detected test reduces this penalty by 0.05, bottoming out at 0.05 for five or more tests. This design reflects the architectural risk of modifying untested code paths.

### Where is the risk scoring logic physically implemented?

The core algorithm resides in **[`code_review_graph/changes.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/changes.py)** within the `compute_risk_score` function, specifically between lines 30 and 74. This module also contains the `analyze_changes` orchestration function and churn calculation utilities that feed data into the scoring system.

### Can detect_changes incorporate git history into risk calculations?

Yes, when the `include_churn` parameter is set to `True` in `analyze_changes`, the system looks up file modification counts from `churn_counts` and adds a weighted factor based on change frequency. This optional churn data adds up to approximately **0.15** additional risk points for frequently modified files, helping identify unstable code hotspots.