Also called Import graph, Module dependency graph, Code dependency graph
In depth Building a dependency graph for any codebase: how imports are parsed and resolved per language, then ranked and clustered.
How a dependency graph is measured
The graph itself is a list of edges. What you measure is its shape:
- Fan-in and fan-out per node: incoming and outgoing edges.
- Cycles: strongly connected components with more than one node, meaning a group in which every member can reach every other.
- Centrality: PageRank scores a node highly when important nodes depend on it, and betweenness finds nodes that sit on many paths between others.
- Communities: clustering algorithms such as Leiden group nodes that depend on each other more than on the rest, which often differs from the folder layout.
A small example:
| File | Imports | Fan-out | Imported by | Fan-in |
|---|---|---|---|---|
api.py | auth.py, db.py | 2 | none | 0 |
auth.py | db.py | 1 | api.py | 1 |
db.py | none | 0 | api.py, auth.py | 2 |
db.py is the load-bearing file. If db.py imported auth.py, the two would form a cycle.
A package graph (npm, pip, Cargo) comes from manifests and lock files and shows what you ship. A code graph comes from parsing imports and shows what the code uses.
Why a dependency graph matters
High fan-in files turn a bug into a system-wide failure, so they deserve more tests and review. Cycles make refactoring all-or-nothing. A node with no incoming edges is a dead code candidate. Communities that cut across folders show where the real module boundaries are, which feeds architecture views such as the C4 model. Function-level edges are a finer layer, the call graph.
How to measure a dependency graph yourself
- JavaScript and TypeScript:
madge --circular src/lists cycles,madge --image graph.svg src/draws the graph, anddepcruise src(dependency-cruiser) adds rules such as "UI must not import data access". - Python:
pydeps mypackagedraws module dependencies. - Go:
go mod graphprints module-level edges. - Rust:
cargo treeprints the crate dependency tree. - Java:
jdepsanalyzes class and package dependencies.
Each is the right pick for a single-language repository, and dependency-cruiser is the better choice if you want to enforce import rules in CI. A repository with several languages has no single tool among these, which is the case repowise targets. See best dependency graph tools for monorepos.
How repowise measures the dependency graph
repowise parses every file with tree-sitter and resolves imports per language into one directed graph of file and symbol nodes. No LLM is involved, so the same code gives the same graph.
- Algorithms: PageRank over the file tier, betweenness for bridges, strongly connected components for cycles, and Leiden clustering (Louvain as fallback, fixed seed). Co-change edges from git are excluded from centrality, so "changes alongside many files" is not mistaken for "many files depend on it".
- Views: an explorable architecture map in the dashboard, C4 views, and
repowise export --format structurizrfor a Structurizr DSL model. - MCP: the graph feeds
get_context,get_riskandget_dead_code. Theget_dependency_pathtool explains how two files connect; enable it with--tools +get_dependency_path.
How it is built is in building a dependency graph for any codebase.