How Community Detection Using the Leiden Algorithm Clusters Code in code-review-graph
The code-review-graph repository implements community detection in code_review_graph/communities.py by constructing a weighted knowledge graph of code entities and applying the Leiden algorithm to optimize modularity, followed by post-processing steps that reassign test nodes and calculate cohesion scores.
The code-review-graph project transforms a codebase into a knowledge graph where functions, classes, and modules become discoverable clusters. When the optional igraph library is present, the system employs the Leiden algorithm to detect these communities, revealing architectural coupling and logical groupings within the source code.
Graph Construction and Edge Weighting
Community detection begins with building a weighted graph that represents code relationships. In code_review_graph/communities.py, the EDGE_WEIGHTS mapping defines the strength of connections between entities:
CALLS = 1.0represents function or method invocationsIMPORTS_FROM = 0.5captures module dependencies
These weights create an undirected igraph.Graph where vertices represent code entities (functions, classes, etc.) and edges reflect structural relationships like calls, imports, and inheritance. The weight list attaches directly to the graph object before clustering begins.
Running the Leiden Algorithm
The core clustering logic resides in the Leiden implementation that optimizes graph modularity. The algorithm configuration in code_review_graph/communities.py prioritizes reproducibility and performance through three key mechanisms:
- Deterministic seeding – Uses the
CRG_LEIDEN_SEEDenvironment variable or defaults to42to guarantee identical partitions across runs - Dynamic resolution – Scales the resolution parameter inversely with the logarithm of the node count, ensuring larger repositories receive appropriately coarser clusters
- Iteration limiting – Caps execution at
n_iterations=2to bound runtime while maintaining community stability
The algorithm operates on the assumption that densely connected subgraphs represent logical code units, separating utility functions from core business logic based on call patterns and import structures.
Post-Processing and Community Refinement
After Leiden generates initial partitions, several refinement steps improve cluster quality and usefulness:
Test node reassignment – The _reassign_test_nodes helper examines test files and moves them to communities containing the most unique subjects they validate, keeping tests logically grouped with their implementation.
Cohesion scoring – The _compute_cohesion_batch function calculates a quantitative metric for each community using the formula internal_edges / (internal + external), measuring how tightly bound the cluster is compared to external connections.
Community naming – The _generate_community_name function derives human-readable labels from file path prefixes, dominant class names, or frequent keywords (e.g., service-auth-token), making raw cluster IDs meaningful to developers.
Size filtering – Clusters below the min_size threshold (default 2) are discarded to eliminate noise from isolated utility functions.
Handling Oversized Communities and Fallbacks
The system includes safeguards against degenerate clusters that capture too much of the codebase:
- Recursive splitting – Communities exceeding a configurable fraction of the total graph trigger
_split_oversized, which applies Leiden recursively to the subgraph - igraph fallback – When the igraph library is unavailable, the system gracefully degrades to file-based grouping by directory depth, ensuring the pipeline completes even in minimal environments
Public API Methods
Two primary entry points expose the community detection functionality:
detect_communities(store, min_size=2) – Runs the complete pipeline including graph construction, Leiden optimization, post-processing, and oversized splitting. Returns a list of community dictionaries containing names, members, and cohesion scores.
incremental_detect_communities(store, changed_files, min_size=2) – Optimizes for CI/CD workflows by re-running detection only when changed files affect existing communities, saving computational work for small incremental updates.
Practical Examples
Detect communities from a codebase using the Leiden algorithm:
from code_review_graph.graph import GraphStore
from code_review_graph.communities import detect_communities
# Build graph from source tree
store = GraphStore.from_path("/path/to/project")
# Detect communities (Leiden requires igraph)
communities = detect_communities(store, min_size=5)
for comm in communities:
print(f"🗂 {comm['name']} (size={comm['size']}, cohesion={comm['cohesion']})")
print(f" Members: {', '.join(comm['members'][:3])}...")
For incremental updates during code reviews:
from code_review_graph.communities import incremental_detect_communities
changed = ["src/api/auth.py", "tests/test_auth.py"]
new_count = incremental_detect_communities(store, changed, min_size=5)
print(f"🔄 Re-detected {new_count} communities")
Visualizing the detected clusters:
from code_review_graph.visualization import plot_communities
plot_communities(store, output_path="architecture_graph.png")
Summary
Community detection using the Leiden algorithm in code-review-graph follows a structured pipeline:
- Constructs weighted graphs from code relationships using
EDGE_WEIGHTSdefinitions incode_review_graph/communities.py - Applies deterministic Leiden clustering with configurable seeds and resolution scaling
- Reassigns test nodes to their corresponding implementation communities via
_reassign_test_nodes - Calculates cohesion scores using
_compute_cohesion_batchto quantify cluster tightness - Generates human-readable names through
_generate_community_name - Splits oversized communities recursively with
_split_oversizedwhile maintaining a file-based fallback
Frequently Asked Questions
How does the Leiden algorithm handle large repositories differently than small ones?
The implementation scales the resolution parameter inversely with the logarithm of the node count in code_review_graph/communities.py. This design choice ensures that larger repositories with thousands of files receive coarser, more manageable clusters rather than fragmented micro-communities, while smaller projects retain fine-grained distinctions between modules.
What happens if igraph is not installed?
The system gracefully falls back to file-based grouping by directory depth. While this produces less precise clusters than the Leiden algorithm's modularity optimization, it guarantees that the detect_communities() function always returns valid community structures even in minimal Python environments without compiled dependencies.
How are test files prevented from forming isolated communities?
The _reassign_test_nodes helper function in code_review_graph/communities.py post-processes the Leiden partitions by analyzing which subjects each test file validates. Test nodes move to the community containing the most unique subjects they test, effectively binding unit tests to their implementation modules rather than leaving them in disconnected clusters based on import similarities.
What determines the quality score assigned to each community?
The cohesion metric calculated by _compute_cohesion_batch measures the ratio of internal edges to total edges (internal plus external). A score approaching 1.0 indicates a highly cohesive cluster where members interact primarily with each other rather than external code, while lower scores suggest boundary-spanning code that bridges multiple architectural concerns.
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 →