# How Graph Diffing Compares Snapshots Over Time in Code-Review-Graph

> Explore how graph diffing in code-review-graph compares JSON snapshots of a knowledge graph to track changes like added nodes, removed edges, and community shifts.

- Repository: [Tirth Kanani/code-review-graph](https://github.com/tirth8205/code-review-graph)
- Tags: deep-dive
- Published: 2026-07-29

---

**Graph diffing in code-review-graph captures the state of a SQLite-backed knowledge graph as JSON snapshots and compares them using deterministic set operations to identify added or removed nodes, edges, and community reassignments.**

The code-review-graph library stores your entire codebase as a structured knowledge graph using SQLite. To track how this graph evolves across commits or releases, the library provides a specialized **graph diffing** system that lets you take point-in-time snapshots and precisely compare them to reveal structural changes between any two states.

## How Graph Diffing Works in Code-Review-Graph

The graph diffing implementation resides in [`code_review_graph/graph_diff.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/graph_diff.py) and operates through four distinct phases: capturing the current state, serializing it to disk, reloading it later, and performing set-based comparisons.

### Taking a Snapshot of Graph State

The `take_snapshot(store)` function extracts the complete state from a `GraphStore` instance. According to the source code in [`graph_diff.py`](https://github.com/tirth8205/code-review-graph/blob/main/graph_diff.py) (lines 15-43), it collects:

- **Global statistics** via `store.get_stats()` for total node and edge counts
- **All nodes** via `store.get_all_nodes(exclude_files=False)` with their qualified names, kinds, file paths, and community assignments
- **Community mappings** via `store.get_all_community_ids()`

The function returns a dictionary containing `node_count`, `edge_count`, a `nodes` mapping (qualified name to metadata), and an `edges` set containing strings in the format `source→target:kind`.

### Persisting Snapshots to Disk

The `save_snapshot(snapshot, path)` function (lines 46-53 in [`graph_diff.py`](https://github.com/tirth8205/code-review-graph/blob/main/graph_diff.py)) serializes the snapshot dictionary to JSON. To ensure **deterministic output**, edge sets are converted to sorted lists before dumping, making file comparisons and version control diffs predictable.

### Loading Saved Snapshots

The `load_snapshot(path)` function (lines 56-61) reverses the process by reading the JSON file and converting edge lists back into Python sets for efficient comparison operations.

### Computing Diffs Between Snapshots

The `diff_snapshots(before, after)` function (lines 64-122) performs the actual comparison using set arithmetic:

- **Node comparison**: Converts the `nodes` mapping keys from both snapshots into sets (`before_nodes` and `after_nodes`), then calculates `new_nodes = after_nodes - before_nodes` and `removed_nodes = before_nodes - after_nodes`
- **Edge comparison**: Performs analogous set differences on the `before_edges` and `after_edges` sets
- **Community tracking**: For nodes present in both snapshots, compares community IDs and records any changes with the node name, previous ID, and new ID

The result dictionary contains:
- `new_nodes` and `removed_nodes` (limited to 100 entries each)
- `new_edges` and `removed_edges` (limited to 100 entries each)
- `community_changes` (limited to 50 nodes)
- `summary` with counters for `nodes_added`, `nodes_removed`, `edges_added`, `edges_removed`, `community_moves`, and total node counts before/after

## Practical Example: Tracking Code Evolution

Here is a complete workflow for using graph diffing to compare snapshots over time:

```python
from pathlib import Path
from code_review_graph.graph import GraphStore
from code_review_graph.graph_diff import take_snapshot, save_snapshot, load_snapshot, diff_snapshots

# 1️⃣ Open a graph (assumes a built graph at .code-review-graph/graph.db)

store = GraphStore(Path(".code-review-graph/graph.db"))

# 2️⃣ Take a snapshot of the current state

snap_before = take_snapshot(store)

# 3️⃣ Persist the snapshot – useful for later comparison or CI storage

save_snapshot(snap_before, Path("snap_before.json"))

# … make code changes, rebuild the graph, or run another analysis …

# 4️⃣ Take a second snapshot after the changes

snap_after = take_snapshot(store)
save_snapshot(snap_after, Path("snap_after.json"))

# 5️⃣ Load snapshots (if they were stored on disk)

snap_before = load_snapshot(Path("snap_before.json"))
snap_after  = load_snapshot(Path("snap_after.json"))

# 6️⃣ Compute the diff

diff = diff_snapshots(snap_before, snap_after)

print("Added nodes:", diff["new_nodes"])
print("Removed edges:", diff["removed_edges"])
print("Community moves:", diff["community_changes"])
print("Summary:", diff["summary"])

```

## Summary

- **Graph diffing** in code-review-graph relies on deterministic JSON snapshots captured via `take_snapshot()` and compared via `diff_snapshots()`.
- The algorithm uses simple **set operations** (difference) on nodes and edges, making it fast and reliable even for large codebases.
- Snapshots capture global statistics, node metadata (including community assignments), and edge signatures in the format `source→target:kind`.
- The `diff_snapshots()` function provides capped results (100 nodes/edges, 50 community changes) to prevent output flooding while preserving summary counts.
- All persistence logic in [`code_review_graph/graph_diff.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/graph_diff.py) ensures deterministic output by sorting edge lists before JSON serialization.

## Frequently Asked Questions

### What file format does code-review-graph use for snapshots?

Snapshots are stored as standard **JSON files**. The format includes sorted lists for edges to ensure deterministic output, making them suitable for version control and CI/CD pipelines where consistent file hashes matter.

### How does the diff algorithm handle community detection changes?

For every node present in both snapshots, `diff_snapshots()` compares community IDs. If a node moved from one community to another, it records the change with the qualified name, previous community ID, and new community ID in the `community_changes` list (capped at 50 entries).

### What are the limits on reported changes in a diff?

The `diff_snapshots()` function limits detailed reporting to **100 new nodes**, **100 removed nodes**, **100 new edges**, **100 removed edges**, and **50 community changes**. However, the `summary` dictionary always reports exact total counts for all categories regardless of these limits.

### Can I use graph diffing without the GraphStore database?

Yes. Once you have saved snapshots to JSON files using `save_snapshot()`, you can load them later with `load_snapshot()` and compare them using `diff_snapshots()` without needing the original SQLite database or `GraphStore` instance available.