Skip to content

Diagrams: Dependency Visualization

Drydock generates four kinds of Mermaid diagrams, each answering a different architectural question. All output is deterministic, safe to share, and bounded to stay legible.

Four Diagram Kinds

Layer Diagrams — "How are modules stacked?"

drydock diagram /project/path --kind layers

Renders topological dependency levels as horizontal bands. Modules sink to lower levels if they have no load-time dependencies; modules rise if they import from others. Edges flow downward, showing stability order.

What it shows: - Depth 0 (foundation) → most stable, least dependent - Depth 1 → depends on depth 0 - Depth N → depends on earlier levels

Good for: Spotting inversions ("a core module importing application code"), understanding build order, finding layers that are too deep (overly stratified design).

Constraints: - Bounded to 60 most-connected modules; rest truncated with "N more" label - Only load-time edges; deferred imports (type-only, test fixtures) are ignored - Deterministic: same graph always produces byte-identical output

Example:

graph TD
    layer0["Depth 0 (3 modules)"]
        id1["models.py"]
        id2["config.py"]
    layer1["Depth 1 (2 modules)"]
        id3["db.py"]
    id3 --> id1
    id3 --> id2
    omitted["1 of 6 modules omitted; showing the most connected"]

Package Diagrams — "How do packages couple?"

drydock diagram /project/path --kind packages

Collapses modules into directories (packages). Shows cross-package dependencies, not within-package. Each node is a directory; edges represent one package depending on another.

What it shows: - Package instability (Ce / (Ca + Ce), where 0 = stable, 1 = unstable) - Afferent coupling (Ca) — how many packages depend on this one - Efferent coupling (Ce) — how many packages this depends on - Dependency violations (unstable package depending on stable one)

Good for: Refactoring decisions ("should this package be split?"), spotting circular dependencies between packages, understanding high-level architecture.

Constraints: - Bounded to 40 most-coupled packages - Node color by instability (not yet implemented; currently monochrome)

Example:

graph LR
    pkg1["api (Ce:2, Ca:1)"]
    pkg2["core (Ce:0, Ca:3)"]
    pkg1 -->|3 imports| pkg2

Seam Diagrams — "Which modules implement this contract?"

drydock diagram /project/path --kind seams --seam "IProcessor"

Shows a single contract and all its implementations, plus their dependencies. Arrows point at the contract, not the reverse — implementers depend on the seam, not the other way around.

What it shows: - Contract (interface, protocol, abstract base) - All implementations - Public symbols in each - Dependencies between implementations

Good for: Understanding plugin architecture, verifying contract compliance, planning extraction.

Constraints: - Focused on one seam; pass --seam CONTRACT to specify - Arrows point at the contract (correct dependency direction for plugins) - Truncated to 50 nodes if very large

Example:

graph TD
    contract["IProcessor (contract)"]
    impl1["FastProcessor"]
    impl2["SlowProcessor"]
    impl1 -->|implements| contract
    impl2 -->|implements| contract
    impl1 -->|uses| impl2

Component Diagrams — "What's inside this component?"

drydock diagram /project/path --kind components

Shows modules and dependencies within a single extracted component. Useful for verifying that extraction didn't miss dependencies or inadvertently included external modules.

What it shows: - All modules in the component - Internal dependencies only - External dependencies marked separately (truncated list)

Good for: QA after extraction, verifying component boundaries, spotting missed dependencies.

Constraints: - Scoped to one component - Bounded to prevent massive graphs

Key Constraints (Why They Matter)

Mermaid has no backslash escape

Problem: A label with a quote " cannot be escaped as \" — Mermaid treats backslash as literal.

Solution: Labels use HTML entities (#quot; for quotes, #lt; and #gt; for angle brackets). Verified against mermaid-cli 11.16.

WRONG:  label["say \"hello\""]    ← Parse error
RIGHT:  label["say #quot;hello#quot;"]

Node ids carry a content hash

Problem: Path segments like a/b.py, a-b.py, a.b.py, and a_b.py all normalize to the same Mermaid id when translated naively. The diagram silently merges unrelated modules.

Solution: Each node id includes a digest of the full path, ensuring distinct modules always map to distinct ids.

a/b.py  → id_abc123_  (distinct id)
a-b.py  → id_def456_  (different)
a_b.py  → id_ghi789_  (different)

Every edge is real

Problem: Inferred or fabricated edges (e.g., "module X might depend on module Y based on naming patterns") make diagrams misleading.

Solution: Every arrow represents an actual import statement in the source code. No inference, no guesses. If an edge isn't in the diagram, the dependency doesn't exist.

Output is bounded

Problem: A layer diagram of a 300-module project produces 1,000+ lines and is unreadable.

Solution: Only the most-connected modules survive the cap (60 for layers, 40 for packages). Truncation is stated in the diagram rather than applied silently:

omitted["47 of 300 modules omitted; showing the most connected"]

You still get the essential structure; less-connected modules aren't shown but their absence is clear.

Seam arrows point at the contract

Problem: If arrows pointed from the contract to implementations, the diagram would suggest that adding a new implementation requires modifying the contract. That's backwards.

Solution: Arrows point at the contract. Implementations depend on the seam; the seam doesn't depend on them. The direction correctly conveys that implementations can be added or removed without changing the contract.

Determinism

Problem: Non-deterministic output makes diagrams hard to version-control, review, or reproduce in CI.

Solution: Same input graph always produces byte-identical Mermaid output. Sorted edge lists, sorted node lists, no randomness or threshold-based variation.

Using Diagrams

Generate and view locally

# Output Mermaid source
drydock diagram /project --kind layers --markdown

# Pipe to mermaid-cli for PNG
drydock diagram /project --kind layers --markdown | mmdc -i /dev/stdin -o diagram.png

# Or paste into GitHub Markdown / Notion / Mermaid Live Editor

Export to PDF/SVG

# Install mermaid-cli if you haven't
npm install -g @mermaid-js/mermaid-cli

# Generate and export
drydock diagram /project --kind layers --markdown | \
  mmdc -i /dev/stdin -o diagram.svg

Share with non-technical stakeholders

Layer diagrams work well for explaining build order and dependencies to product managers or stakeholders. Seam diagrams are great for showing plugin architecture in design reviews.

Integrate into documentation

Embed diagram Mermaid in your project README or Wiki:

## Architecture

```mermaid
<paste output from `drydock diagram ... --markdown` here>
```

Common Pitfalls

"The diagram is too big"

If output is truncated, you have more than 60–40 of the most-connected nodes. This often means:

  1. Too coarse a granularity — Try the package diagram instead of the layer diagram
  2. Too fine a granularity — Consider merging small utility modules
  3. Missing components — You have a large monolith; use drydock boundaries to find natural extraction points

"The diagram looks wrong"

Before assuming a bug:

  1. Check the source — Are the imports really there? Run drydock codemap to see what the dependency analyzer found
  2. Check the filter — Layer diagrams exclude deferred imports by default (type hints, test fixtures). Might your dependencies be deferred?
  3. Check for cycles — Run drydock architecture --markdown to see if dependency cycles are causing unexpected layer assignment

"I see modules that shouldn't be there"

Module inclusion is based on the dependency graph, not your mental model. If a module appears:

  1. Verify the import — Search the source code for the import statement
  2. Check if it's a test — Did you exclude test files? Use --include-tests=false
  3. Check scope — Is it in a language you disabled? Run drydock languages to see what's included

Technical Details

Rendering Pipeline

  1. Build graph — Walk source tree, extract imports, resolve to modules
  2. Compute metrics — Afferent/efferent coupling, instability, depth
  3. Render diagram — Call layer/package/seam/component function, emit Mermaid
  4. Escape labels — HTML-entity-escape special characters
  5. Validate — Ensure all edges reference declared nodes (test mode only)

Performance

Diagram generation is typically sub-second for projects up to 500 modules. Larger projects may take a few seconds (mostly time spent computing metrics, not rendering Mermaid).

Diagram Kinds in API

For programmatic use (building a GUI, composing with other tools):

from drydock.diagram.mermaid import (
    layer_diagram,
    package_diagram,
    seam_diagram,
    component_diagram,
)
from drydock.arch.graph import DependencyGraph, build_graph
from drydock.core.registry import AnalysisContext

ctx = AnalysisContext("/path/to/project")
graph = build_graph(ctx)

# Generate diagrams
layers = layer_diagram(graph, max_nodes=60)
packages = package_diagram(graph, max_nodes=40)

# For seams, you need the analysis context and a Seam object
# (typically obtained from architecture analysis results)

All functions return Mermaid source as a string (no fences).

Validation

All 48 generated diagrams in Drydock's regression suite (four diagram kinds, across eleven adversarial fixtures with cycles, empty modules, deep nesting, etc.) render cleanly through mermaid-cli 11.16.

See Also