A fast, multi-language dependency graph analyzer that catches structural regressions in CI.
Untangle builds module-level dependency graphs from your source code, computes structural complexity metrics, and diffs them between git revisions. Add it to your CI pipeline to fail PRs that introduce circular dependencies or increase coupling.
Version 0.4.0 adds a layered architecture view and consolidates the CLI around analyze submodes for report, graph, and architecture projections.
| Language | Granularity | Parser |
|---|---|---|
| Python | Module (file-level) | tree-sitter |
| Ruby | File-level | tree-sitter |
| Go | Package-level | tree-sitter |
| Rust | Module (file-level) | tree-sitter |
# Analyze current state
untangle analyze report ./src --lang python
# Export the raw dependency graph
untangle analyze graph ./src --lang go --format dot | dot -Tsvg -o deps.svg
# Project a layered architecture view
untangle analyze architecture ./src --lang python --format dot | dot -Tsvg -o architecture.svg
# Check projected architecture against policy
untangle analyze architecture-check ./src --lang python --format text
# Generate a starter architecture policy
untangle analyze architecture-init ./src --lang python --level 1
# Diff against main (the CI use case)
untangle diff --base origin/main --head HEAD --fail-on fanout-increase,new-scc
# Compute CRAP (complexity + coverage) for functions
untangle quality functions ./src --metric crap --coverage lcov.info --lang rust --format text
# Generate the unified engineer-facing quality report
untangle quality report ./src --coverage lcov.info --lang rust --format textuntangle analyze report [path]prints the standard structural report intext,json, orsarif.untangle analyze graph [path]exports the raw dependency graph indotorjson.untangle analyze architecture [path]projects a layered architecture view indotorjson.untangle analyze architecture-check [path]validates projected component boundaries intextorjson.untangle analyze architecture-init [path]generates a starter architecture policy in.untangle.toml.untangle diff [path]compares two git revisions and supports CI policy gating.untangle quality functions [path]reports function-level quality metrics.untangle quality report [path]combines structural metrics, hotspots, function quality, architecture analysis, and priority actions.untangle quality project [path]keeps the legacy aggregate quality summary.untangle service-graph [path]derives service dependencies from[services]config plus API metadata.untangle config show [path]anduntangle config explain [path]inspect merged configuration.
- Fan-out / Fan-in — how many modules does each module depend on (and how many depend on it)?
- Fan-out entropy — Shannon entropy of outgoing edges. Distinguishes "depends on 10 things equally" from "depends on 10 things but 90% on one."
- Strongly connected components — circular dependency clusters. The hardest structural debt to unwind.
- SCC-adjusted entropy — penalises complexity inside circular clusters.
- Edge provenance — every edge carries the source module path and import line/column. (For Go, the path is the package directory.)
- name: Dependency structure check
run: |
untangle diff --base origin/main --head ${{ github.sha }} \
--fail-on fanout-increase,new-scc \
--format json > untangle-diff.json| Exit Code | Meaning |
|---|---|
0 |
No policy violations |
1 |
One or more --fail-on conditions triggered, or an error occurred |
| Condition | Triggers when |
|---|---|
fanout-increase |
Any module's fan-out increased |
fanout-threshold |
Any module exceeds --threshold-fanout |
new-scc |
A new circular dependency cluster appeared |
scc-growth |
An existing circular cluster gained members |
entropy-increase |
Graph-level mean entropy increased |
new-edge |
Any new dependency edge was added (strict mode) |
new-architecture-violation |
A projected component boundary violation appears in head but not base |
new-architecture-cycle |
A new projected component cycle appears in head |
architecture-cycle-growth |
A projected component cycle in head grew relative to base |
Create a .untangle.toml in your project root:
[defaults]
lang = "python"
quiet = false
include_tests = false
[targeting]
exclude = ["vendor/**", "**/test/**", "**/*_test.go"]
[analyze.report]
format = "json"
top = 20
insights = "auto"
[analyze.graph]
format = "dot"
[analyze.architecture]
format = "dot"
level = 1
check_format = "text"
fail_on_violations = true
fail_on_cycles = true
[analyze.architecture.allowed_dependencies]
api = ["db", "utils"]
db = []
utils = []
[diff]
format = "json"
fail_on = ["fanout-increase", "new-scc", "scc-growth", "new-architecture-violation"]
[quality]
format = "text"$ untangle analyze report ./src --lang python --format text
Modules: 342 Edges: 1,208 Density: 0.010
Fan-out: mean 3.5 · p90 8 · max 23
SCCs: 4 (29 modules in circular clusters, largest: 12)
Top fan-out:
src/core/engine.py 23 (entropy: 4.12)
src/api/middleware.py 18 (entropy: 3.89) ⚠ SCC #0 (12 members)
src/handlers/dispatch.py 15 (entropy: 3.74)
2 files skipped (syntax errors), 14 unresolved imports
Completed in 0.85s (403 modules/sec)
# From crates.io
cargo install untangle
# From source
git clone https://github.com/jonochang/untangle && cd untangle
cargo build --releasePre-built binaries for Linux, macOS, and Windows are available on the releases page.
Nix users can also build from package.nix.
Untangle uses tree-sitter to parse source files into concrete syntax trees, then extracts import statements via language-specific S-expression queries. This means it handles files with syntax errors gracefully (partial parse) and runs at >10,000 files/sec.
The extracted imports are resolved to project-internal modules and assembled into a directed graph using petgraph. Metrics are computed using Tarjan's algorithm for SCCs and standard graph traversal for fan-out/fan-in.
For diff, untangle reads files at arbitrary git refs via libgit2 without checking out branches, keeping the operation fast and non-destructive.
- Requirements specification — what the tool does, output formats, failure modes
- Technical design — Rust implementation plan, data model, testing strategy
- API design v2 — command model, config layout, and JSON contract
MIT OR Apache-2.0