Skip to content
Arxo Arxo

Migration Guide

This guide helps you migrate from other architecture analysis tools to Arxo, with side-by-side comparisons and practical examples.

  • Multi-language support - TypeScript, JavaScript, Python, Rust, Java, Kotlin, Go
  • 50+ metrics - Beyond cycle detection: coupling, centrality, hotspots, evolution
  • Policy enforcement - Define and enforce architectural rules in CI
  • Built-in presets - Quick, CI, risk, security, AI/LLM analysis
  • Performance - Written in Rust, optimized for large codebases
  • Rich output - Console, JSON, HTML, SARIF, MessagePack formats

Madge is popular for detecting circular dependencies in JavaScript/TypeScript projects.

FeatureMadgeArxo
Cycle detection✅ Yes✅ Yes (SCC metric)
Dependency graph✅ Yes✅ Yes (import + call graphs)
Visualization✅ Graphviz✅ HTML interactive
Orphan detection✅ Yes⚠️ Partial (use centrality tables / graph views)
Policy enforcement❌ No✅ Yes
Multi-language❌ No✅ Yes (7 languages)
Performance metrics❌ No✅ Yes (50+ metrics)
CI/CD integration⚠️ Manual✅ Built-in

Madge:

Terminal window
madge --circular src/

Arxo:

Terminal window
arxo analyze --metric scc

Madge:

Terminal window
madge --image graph.png src/

Arxo:

Terminal window
arxo analyze --format html --output report.html
# Open report.html for interactive graph

Madge:

Terminal window
madge --orphans src/

Arxo (closest equivalent):

Terminal window
arxo analyze --metric centrality --format html --output report.html
# In the centrality module table, inspect modules with fan_in = 0

Madge:

Terminal window
madge --circular --exclude '^(node_modules|test)' src/

Arxo (.arxo.yaml):

data:
import_graph:
exclude:
- "**/node_modules/**"
- "**/test/**"
- "**/*.test.ts"

Then run:

Terminal window
arxo analyze --metric scc

Madge:

Terminal window
madge --circular --extensions ts,tsx src/

Arxo (auto-detects all TypeScript files):

Terminal window
arxo analyze --metric scc

Madge (manual exit code check):

#!/bin/bash
if madge --circular src/ | grep -q "Circular"; then
echo "Cycles detected!"
exit 1
fi

Arxo (built-in CI preset):

Terminal window
arxo analyze --preset ci --fail-fast

If you have a madge.json:

{
"detectiveOptions": {
"ts": {
"skipTypeImports": true
}
},
"fileExtensions": ["ts", "tsx"],
"excludeRegExp": ["node_modules", "test"]
}

Equivalent .arxo.yaml:

data:
import_graph:
exclude:
- "**/node_modules/**"
- "**/test/**"
languages:
- typescript
metrics:
- id: scc
policy:
invariants:
- metric: scc.max_cycle_size
op: "=="
value: 0
message: "No circular dependencies allowed"

dependency-cruiser is a comprehensive dependency validation tool for JavaScript/TypeScript.

Featuredependency-cruiserArxo
Dependency rules✅ Yes✅ Yes (policy invariants)
Cycle detection✅ Yes✅ Yes
Path validation✅ Yes✅ Yes (layer violations)
TypeScript support✅ Yes✅ Yes
Workspace support✅ Yes✅ Yes (monorepo)
Multi-language❌ No✅ Yes
Evolution metrics❌ No✅ Yes (git-based)
Performance⚠️ Slow on large repos✅ Fast (Rust)

dependency-cruiser:

Terminal window
depcruise --validate .dependency-cruiser.js src

Arxo:

Terminal window
arxo analyze --preset ci

dependency-cruiser (.dependency-cruiser.js):

module.exports = {
forbidden: [
{
name: 'no-circular',
severity: 'error',
from: {},
to: {
circular: true
}
}
]
};

Arxo (.arxo.yaml):

policy:
invariants:
- metric: scc.max_cycle_size
op: "=="
value: 0
message: "No circular dependencies allowed"

dependency-cruiser:

module.exports = {
forbidden: [
{
name: 'no-ui-to-data',
from: { path: '^src/ui' },
to: { path: '^src/data' }
}
]
};

Arxo (.arxo.yaml):

architecture:
layers:
- name: ui
paths: ["src/ui/**"]
allowed_effects: []
can_depend_on: ["services"]
- name: services
paths: ["src/services/**"]
allowed_effects: []
can_depend_on: ["data"]
- name: data
paths: ["src/data/**"]
allowed_effects: []
can_depend_on: []
metrics:
- id: layer_violations
policy:
invariants:
- metric: layer_violations.violations_count
op: "=="
value: 0
message: "Layer violations detected"

dependency-cruiser:

module.exports = {
forbidden: [
{
name: 'no-inter-app',
from: { path: '^apps/app1' },
to: { path: '^apps/app2' }
}
]
};

Arxo (.arxo.yaml):

data:
import_graph:
group_by: folder
group_depth: 2 # apps/app-name
policy:
invariants:
- metric: smells.cycles.count
op: "=="
value: 0
message: "Apps should not depend on each other"

dependency-cruiser:

module.exports = {
forbidden: [
{
name: 'no-deprecated',
to: {
dependencyTypes: ['deprecated']
}
}
]
};

Arxo (uses OSV integration):

metrics:
- id: security
policy:
invariants:
- metric: security.vulnerability_count
op: "=="
value: 0
message: "No known vulnerabilities allowed"
dependency-cruiser ruleArxo equivalent
circular: truescc.max_cycle_size == 0
path restrictionslayer_violations metric
dependencyTypes: ['deprecated']security metric
orphan: trueNo direct invariant; inspect centrality.module.nodes_table (fan_in == 0)
reachable: falseruntime_drift metric (dead code)

eslint-plugin-import catches import issues in JavaScript/TypeScript.

Featureeslint-plugin-importArxo
No unresolved imports✅ Yes✅ Yes (parsing)
No cycles✅ Yes✅ Yes (SCC)
Import order✅ Yes❌ No (use prettier)
Path validation⚠️ Limited✅ Yes (layers)
Multi-file analysis⚠️ Slow✅ Fast
Architecture metrics❌ No✅ Yes

ESLint:

{
"rules": {
"import/no-unresolved": "error"
}
}

Arxo (automatic):

Terminal window
arxo analyze
# Parsing errors shown automatically

ESLint:

{
"rules": {
"import/no-cycle": "error"
}
}

Arxo:

policy:
invariants:
- metric: scc.max_cycle_size
op: "=="
value: 0

ESLint:

{
"rules": {
"import/no-restricted-paths": ["error", {
"zones": [
{
"target": "./src/ui",
"from": "./src/data"
}
]
}]
}
}

Arxo:

architecture:
layers:
- name: ui
paths: ["src/ui/**"]
allowed_effects: []
can_depend_on: ["services"]
- name: services
paths: ["src/services/**"]
allowed_effects: []
can_depend_on: ["data"]
- name: data
paths: ["src/data/**"]
allowed_effects: []
can_depend_on: []
metrics:
- id: layer_violations
policy:
invariants:
- metric: layer_violations.violations_count
op: "=="
value: 0

If you use ADRs for documenting architecture, Arxo policies can enforce those decisions:

ADR: “We decided to keep services decoupled (propagation cost < 0.15)”

Arxo:

policy:
invariants:
- metric: propagation_cost.system.ratio
op: "<="
value: 0.15
message: "Services should be loosely coupled (ADR-001)"

If you use Nx @nrwl/nx/enforce-module-boundaries:

Nx (.eslintrc.json):

{
"rules": {
"@nrwl/nx/enforce-module-boundaries": ["error", {
"depConstraints": [
{
"sourceTag": "scope:app1",
"onlyDependOnLibsWithTags": ["scope:shared"]
}
]
}]
}
}

Arxo (.arxo.yaml):

data:
import_graph:
group_by: folder
group_depth: 2
metrics:
- id: monorepo
policy:
invariants:
- metric: monorepo.policy.edge_violations_count
op: "=="
value: 0

Document what your current tool checks:

Terminal window
# Example: List all madge/depcruise rules
grep -r "circular\|orphan\|forbidden" .
Terminal window
arxo init
arxo analyze --preset quick

Review the output to understand your architecture’s current state.

Start with loose policies, then tighten:

# Week 1: Baseline
policy:
invariants:
- metric: scc.max_cycle_size
op: "<="
value: 10 # Allow small cycles initially
# Week 4: Tighten
policy:
invariants:
- metric: scc.max_cycle_size
op: "=="
value: 0 # Zero cycles
.github/workflows/architecture.yml
- name: Run Architecture Analysis
run: arxo analyze --preset ci --fail-fast

Once Arxo is stable in CI:

  1. Remove old tool from package.json
  2. Delete old config files (.dependency-cruiser.js, etc.)
  3. Update documentation

TaskMadgeArxo
Find cyclesmadge --circular src/arxo analyze --metric scc
Get detailsmadge --circular --json src/arxo analyze --format json
Visualizemadge --image graph.png src/arxo analyze --format html
Fix guidance❌ ManualBreaking Cycles Guide
Taskdependency-cruiserArxo
Run checkdepcruise --validatearxo analyze --preset ci
Fail on errorExit code 1--fail-fast
Cache results❌ Manual✅ Built-in
Generate report--output-type html--format html

madge.json:

{
"exclude": "node_modules|test",
"circular": true,
"detectiveOptions": {
"ts": { "skipTypeImports": true }
}
}

arxo.yaml:

data:
import_graph:
exclude:
- "**/node_modules/**"
- "**/test/**"
metrics:
- id: scc
policy:
invariants:
- metric: scc.max_cycle_size
op: "=="
value: 0

.dependency-cruiser.js:

module.exports = {
forbidden: [
{ name: 'no-circular', to: { circular: true } },
{ name: 'no-orphans', from: { orphan: true } }
],
options: {
exclude: 'node_modules'
}
};

arxo.yaml:

data:
import_graph:
exclude:
- "**/node_modules/**"
metrics:
- id: scc
- id: centrality
policy:
invariants:
- metric: scc.max_cycle_size
op: "=="
value: 0

Then review centrality.module.nodes_table and check modules with fan_in = 0 for orphan-like patterns.

If you previously used flat flow_analysis.* keys, migrate to domain-scoped keys:

v1 keyv2 key
flow_analysis.nodesflow_analysis.graph.node_count
flow_analysis.linksflow_analysis.graph.edge_count
flow_analysis.total_flowflow_analysis.graph.total_weight
flow_analysis.avg_degreeflow_analysis.graph.avg_out_weight
flow_analysis.max_fan_inflow_analysis.graph.max_fan_in_weight
flow_analysis.max_fan_outflow_analysis.graph.max_fan_out_weight
flow_analysis.gini_fan_inflow_analysis.graph.gini_fan_in
flow_analysis.gini_fan_outflow_analysis.graph.gini_fan_out
flow_analysis.densityflow_analysis.graph.density

Flow Analysis now also includes dedicated domains:

  • flow_analysis.bow_tie.*
  • flow_analysis.bottleneck.*
  • flow_analysis.logical.*
  • flow_analysis.runtime.*

See Flow Analysis for the full key contract and profile behavior.


Based on a typical TypeScript monorepo (500 files, 3 apps):

ToolInitial RunCached RunMemory
madge~8s~8s (no cache)150MB
dependency-cruiser~25s~20s300MB
Arxo~3s~0.5s80MB

Arxo is faster due to:

  • Rust implementation
  • Parallel metric computation
  • Incremental analysis cache
  • Optimized graph algorithms

  • Install Arxo (pre-built binary from Releases or Installation)
  • Create config: arxo init
  • Run initial analysis: arxo analyze --preset quick
  • Review current architecture state
  • Define policies in .arxo.yaml
  • Add to CI pipeline
  • Test CI integration on a branch
  • Document policies for team
  • Remove old tool from package.json
  • Delete old config files
  • Update team documentation

Cause: Arxo is more comprehensive. Old tools may have missed issues.

Solution: Start with loose policies, review each violation, tighten gradually.

Cause: Grouping strategy. File-level vs folder-level grouping affects counts.

Solution: Adjust group_by and group_depth:

data:
import_graph:
group_by: file # or folder
group_depth: 2

Cause: Running too many metrics.

Solution: Use presets or limit metrics:

Terminal window
arxo analyze --quick # Fast check
arxo analyze --metric scc # Just cycles


After migrating, explore advanced features:

  • Evolution metrics - Track code churn with --metric msr
  • Coupling analysis - Find tight coupling with --preset coupling
  • Refactoring suggestions - Get automated suggestions with --metric refactoring_recommendations
  • Baseline tracking - Compare against main with --baseline origin/main
  • AI analysis - Check LLM integration health with --preset ai