Introduction

As software projects grow from a handful of modules to hundreds or thousands of interconnected components, understanding the dependency graph becomes critical for maintenance, refactoring, and onboarding. A tangled dependency web leads to fragile builds, circular imports, and architectural drift that silently degrades code quality over time. Self-hosted dependency graph visualization platforms give teams a real-time, interactive view of their codebase architecture — highlighting tight coupling, circular dependencies, and architectural violations before they become production incidents.

This guide compares three approaches to self-hosted dependency visualization: programmatic dependency graph generators like Dependency Cruiser, architecture linting platforms, and integrated architecture visualization servers. Each serves a different layer of the dependency management stack.

Why Visualize Dependencies?

Understanding your codebase’s dependency structure is essential for:

  • Architectural governance: Ensure modules conform to your intended architecture (e.g., layered, hexagonal, feature-based). Without visualization, architectural drift is invisible until it causes a crisis.
  • Build optimization: Identify tightly coupled components that slow down parallel builds or force unnecessary recompilation. Projects with poor dependency hygiene can see CI times balloon by 3-5x.
  • Refactoring safety: Before extracting a module or inverting a dependency, you need to know every consumer. A dependency graph answers this question instantly — no grep or IDE search required.
  • Onboarding acceleration: New team members can explore the codebase visually rather than reading thousands of files sequentially. A picture of your architecture is worth weeks of code reading.

Comparison Table: Dependency Visualization Approaches

FeatureDependency CruiserArchitecture Linting (eslint-plugin-import)Graph-Based Architecture
TypeCLI + VisualizerESLint PluginMulti-Tool Pipeline
GitHub Stars6,7711,700+ (eslint-plugin-import)N/A (composition)
LanguagesJS/TS/CoffeeScriptJavaScript/TypeScriptMulti-language (via adapters)
Output FormatsDOT, SVG, HTML, JSONConsole/JSONDOT, PNG, SVG, HTML
InteractiveHTML report with filteringNoYes (via Graphviz/web)
Rule ConfigurationExtensive (regex, module)Import rulesCustom (per-tool)
Docker SupportYes (official image)No (npm only)Yes (composed)
Architecture EnforcementYes (rules-as-code)Yes (lint rules)Yes (graph analysis)
Self-Hosted DashboardVia HTML export + nginxNoYes (custom web UI)

Dependency Cruiser: Rules-Based Architecture Analysis

Dependency Cruiser is the most mature and widely used dependency analysis tool in the JavaScript ecosystem, with over 6,700 GitHub stars. It goes beyond simple visualization — it’s a rules engine that validates your dependency graph against architectural constraints you define.

Key Features

  • Rules as code: Define allowed/disallowed dependencies with regex patterns on module names and paths
  • Caching: Incremental analysis that only re-checks changed files, making it fast even on 10,000+ module codebases
  • Multiple output formats: Generate DOT files for Graphviz, interactive HTML reports, SVG diagrams, and terminal-based summaries
  • Custom reporters: Write your own output format or integrate with CI/CD platforms via JSON output
  • TypeScript support: First-class TS support including path aliases and project references

Docker Deployment with HTML Dashboard

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
version: "3.8"
services:
  dependency-cruiser:
    image: node:20-slim
    container_name: depcruiser
    working_dir: /workspace
    volumes:
      - ./your-codebase:/workspace:ro
      - ./depcruiser-reports:/reports
      - ./.dependency-cruiser.js:/workspace/.dependency-cruiser.js:ro
    command: >
      sh -c "npx dependency-cruiser --config .dependency-cruiser.js
             --output-type html src > /reports/dependency-graph.html"
    
  nginx-viewer:
    image: nginx:alpine
    container_name: dep-viewer
    ports:
      - "8080:80"
    volumes:
      - ./depcruiser-reports:/usr/share/nginx/html:ro
    restart: unless-stopped

Example .dependency-cruiser.js rule configuration:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
module.exports = {
  forbidden: [
    {
      name: 'no-circular-deps',
      severity: 'error',
      from: { path: '^src/' },
      to: { circular: true }
    },
    {
      name: 'ui-cannot-import-data-layer',
      severity: 'error',
      from: { path: '^src/components/' },
      to: { path: '^src/data-access/' }
    },
    {
      name: 'shared-no-domain-imports',
      severity: 'warn',
      from: { path: '^src/shared/' },
      to: { path: '^src/features/', dependencyTypes: ['local'] }
    }
  ],
  options: {
    doNotFollow: { path: 'node_modules' },
    tsPreCompilationDeps: true,
    tsConfig: { fileName: 'tsconfig.json' }
  }
};

Architecture Linting: Enforcing Dependency Rules at Build Time

Architecture linting takes a different approach: instead of visualizing dependencies, it enforces them. ESLint’s import plugin and similar tools for other languages catch dependency violations during development and CI.

Multi-Language Architecture Linting

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
version: "3.8"
services:
  arch-lint:
    image: node:20-slim
    container_name: arch-lint
    working_dir: /workspace
    volumes:
      - ./your-codebase:/workspace:ro
      - ./arch-lint-reports:/reports
    command: >
      sh -c "npx eslint --rule 'import/no-restricted-paths: [error, {
        zones: [{
          target: './src/components',
          from: './src/services',
          message: "Components must not import from services layer"
        }]
      }]' src/ --format json > /reports/arch-violations.json"

Building a Self-Hosted Architecture Dashboard

For teams that want an always-on, interactive architecture visualization dashboard, you can compose Dependency Cruiser with a web frontend:

Complete Architecture Dashboard Stack

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
version: "3.8"
services:
  analyzer:
    image: node:20-slim
    container_name: dep-analyzer
    volumes:
      - ./codebase:/workspace:ro
      - ./reports:/reports
    entrypoint: >
      sh -c "while true; do
        npx dependency-cruiser --output-type dot /workspace/src > /reports/graph.dot
        dot -Tsvg /reports/graph.dot > /reports/graph.svg
        dot -Tpng /reports/graph.dot > /reports/graph.png
        sleep 3600
      done"
    restart: unless-stopped

  nginx:
    image: nginx:alpine
    ports:
      - "8080:80"
    volumes:
      - ./reports:/usr/share/nginx/html:ro
      - ./dashboard.html:/usr/share/nginx/html/index.html:ro

This setup regenerates the architecture graph hourly, making it available as an always-on internal dashboard for developers.

Integration with CI/CD Pipelines

Dependency analysis is most effective when integrated into your CI pipeline. Here’s a GitHub Actions workflow that generates and archives architecture graphs on every push:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
name: Architecture Analysis
on: [push, pull_request]
jobs:
  dep-graph:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
      - run: npx dependency-cruiser --output-type err-html src > dep-report.html
      - run: npx dependency-cruiser --output-type dot src | dot -Tpng > arch-graph.png
      - uses: actions/upload-artifact@v4
        with:
          name: architecture-analysis
          path: |
            dep-report.html
            arch-graph.png

Why Self-Host Your Dependency Analysis Infrastructure?

Intellectual Property Protection: Your dependency graph IS your architecture. Uploading it to a SaaS service means sending a complete map of your codebase — internal module names, proprietary algorithms organized by directory, and the relationships between business-critical components — to a third party. Self-hosting keeps this sensitive architectural information entirely within your infrastructure.

Continuous Integration Without Rate Limits: Commercial dependency analysis services typically limit the number of projects, team members, or analysis runs per billing period. A self-hosted Dependency Cruiser instance can run on every commit, on every branch, for every project in your monorepo — with zero incremental cost. For organizations with 20+ active repositories, the cost difference between self-hosted and SaaS can exceed $10,000 annually.

Custom Rules That Match Your Architecture: Every codebase has unique architectural conventions — naming patterns for internal packages, allowed cross-module reference patterns, layers that are specific to your domain. Self-hosted tools let you encode these conventions as machine-enforceable rules that developers can run locally before pushing. Our code quality platform comparison covers complementary tools for enforcing standards at scale.

Integration with Your Existing Developer Workflow: A self-hosted dependency dashboard becomes part of your internal developer platform, accessible alongside your CI dashboards, documentation, and code review tools. Developers can check the architecture graph before making significant changes, reducing architectural review cycles. For team-level infrastructure, our self-hosted developer portal guide shows how to build a unified platform.

FAQ

Can dependency analysis tools handle polyglot (multi-language) codebases?

Dependency Cruiser is JavaScript/TypeScript-specific, but you can compose multiple language-specific tools. For Python, pydeps generates dependency graphs. For Java, jdeps comes with the JDK. For polyglot projects, write a wrapper script that runs the appropriate tool per directory and merges the DOT output. Tools like Graphviz can then render the combined graph.

How do I handle false positives from architectural rules?

Dependency Cruiser supports an allowed rules section alongside forbidden rules. You can whitelist specific dependency paths that are acceptable exceptions to your architecture. Additionally, use the dependencyTypes filter to distinguish between direct imports and type-only imports (TypeScript import type), which often represent legitimate cross-boundary references without runtime coupling.

What’s the performance impact on large codebases?

Dependency Cruiser caches analysis results by file hash, so subsequent runs on unchanged codebases complete in under 2 seconds even on 10,000+ module projects. First-time analysis takes approximately 1-2 seconds per 1,000 modules. For very large monorepos (50,000+ modules), consider running analysis per-package rather than globally and aggregating results.

Can I use these tools to track architecture changes over time?

Yes. Archive the dependency graph JSON output from each CI run and use a simple diff script to detect new dependencies, removed dependencies, or changed dependency directions between commits. Tools like graphviz-diff can visually highlight changes between two architecture snapshots. For our guide on infrastructure monitoring that pairs well with architecture tracking, see our distributed tracing comparison.

What’s the difference between static analysis and runtime dependency tracking?

Static analysis (what Dependency Cruiser and linters do) examines import statements to determine dependencies at the module/file level. This captures the intended architecture. Runtime dependency tracking (via OpenTelemetry, service meshes) captures actual call patterns between services. Both are complementary — static analysis prevents architectural violations before deployment, while runtime tracking catches unexpected inter-service communication patterns in production.


💰 想测试你的市场判断力?我用 Polymarket 做预测市场交易——这是全球最大的预测市场平台,从大选结果到技术监管时间线,什么都可以押注。和赌博不同,这是真正的信息市场:你懂的信息越多,胜率越高。我靠预测技术相关事件的走向已经赚了不少。用我的邀请链接注册:Polymarket.com