Interpretation
Reading Your Results
Section titled “Reading Your Results”How to interpret cycle detection results, set thresholds, and prioritize fixes. For an overview, see Circular Dependencies.
Why Cycles Matter
Section titled “Why Cycles Matter”Circular dependencies create real problems in day-to-day development:
- Harder to reason about — circular logic is difficult to follow
- Can’t test in isolation — pulling in one module pulls in the whole cycle
- Prevents tree-shaking — bundlers can’t eliminate dead code in cycles
- Blocks incremental builds — changes affect everything in the cycle
- Increases coupling — all modules in a cycle are tightly bound
- Slows down builds — compilers can’t parallelize efficiently
Real-World Example
Section titled “Real-World Example”Prettier (4,883 files):
# What the team assumed"Our architecture is pretty clean"
# What Arxo foundscc.max_cycle_size: 56 # 56 files in one giant cycle!scc.total_nodes_in_cycles: 79 # 79 files stuck in cycles
# The largest cyclesrc/standalone.js → src/cli/prettier-internal.js → src/cli/utilities.js → ... (52 more files) → src/index.js → back to src/standalone.js
# Impact- 56 files can't be tested independently- Changes in CLI propagate to standalone and back- Bundle size is bloated (no tree-shaking possible)Thresholds
Section titled “Thresholds”| Metric | Healthy | Needs attention | Warning | Critical |
|---|---|---|---|---|
scc.max_cycle_size | 0 (no cycles) | 2 (two files importing each other) | 3–5 files | 5+ files |
scc.total_nodes_in_cycles | 0 | 1–5 files | 5–10 files | 10+ files |
scc.cycle_count | 0 | 1–2 cycles | 3–5 cycles | 5+ cycles |
scc.component_count | Equal to file count | 10% below file count | 20% below | 30%+ below |
Tip: scc.component_count should ideally equal your total module count. If you have 100 modules but scc.component_count = 95, there are 5 cycles merging modules together.
Reading the Output
Section titled “Reading the Output”Healthy Codebase
Section titled “Healthy Codebase”{ "scc": { "component_count": 1250, "max_cycle_size": 0, "cycle_count": 0, "total_nodes_in_cycles": 0 }}Every module has a clear dependency direction. No cycles.
Needs Attention
Section titled “Needs Attention”{ "scc": { "component_count": 980, "max_cycle_size": 8, "cycle_count": 5, "total_nodes_in_cycles": 24 }}Multiple cycles present. The largest has 8 files — medium severity. 24 files are affected total.
Critical
Section titled “Critical”{ "scc": { "component_count": 450, "max_cycle_size": 56, "cycle_count": 12, "total_nodes_in_cycles": 150 }}Severe problems. A 56-file cycle needs immediate attention.
Understanding Cycle Details
Section titled “Understanding Cycle Details”For each cycle, Arxo shows:
- Files in the cycle — which modules are involved
- Internal edges — the dependencies within the cycle
- Boundary connections — what depends on this cycle, and what it depends on
- Per-file metrics — centrality, churn, and refactoring priority
Per-File Priority Table
Section titled “Per-File Priority Table”When git history is enabled (use_git_history: true), each file in a cycle gets a priority score:
| Column | What it means | How to use it |
|---|---|---|
node_id | File path | Identifies the file |
centrality | How central this file is within the cycle (0–1) | High = bottleneck |
churn | How often this file changes (total commits) | High = frequently modified |
hotspot_score | churn × centrality | Sort by this — higher = fix first |
Example:
{ "node_table": [ { "node_id": "src/cli/prettier.js", "centrality": 0.24, "churn": 87, "hotspot_score": 20.88 // Fix this first }, { "node_id": "src/standalone.js", "centrality": 0.15, "churn": 23, "hotspot_score": 3.45 } ]}Cycle-Cut Candidates
Section titled “Cycle-Cut Candidates”Arxo suggests which dependency to remove to break each cycle — starting with the cheapest option:
{ "cycle_cut_candidates": [ { "from": "src/document/builders/join.js", "to": "src/document/utilities/assert-doc.js", "call_count": 2, "note": "Cheapest edge to cut in this cycle" } ]}How to use:
- Start with the edge with the lowest call count — it’s the easiest to refactor
- The
from → todependency is the one to eliminate - See Fixing Cycles and Refactoring Patterns for how
Next Steps
Section titled “Next Steps”- Cycle Microscope — Per-cycle details, node table, boundary edges
- Git History Integration — Enable churn and hotspot_score for refactor priority
- Fixing Cycles — Step-by-step process
- Refactoring Patterns — Concrete code patterns
- Circular Dependencies — Back to overview