#1214·diagrams

Default curvestyle / splines='ortho' both produce broken edges on dense diagrams (upstream graphviz #1415 + #1856)

Author: bmd1905Created May 5, 2026Updated May 5, 2026

Summary

Both of the edge-routing modes a typical user reaches for in diagrams produce broken output on real-world dense, cluster-heavy architecture diagrams (20-30 nodes, 5-8 Clusters, fan-in to shared services). The default curvestyle="spline" (and the popular alternative curvestyle="curved") produce visually unreadable spaghetti, and the obvious "let me just use orthogonal routing" workaround — graph_attr={"splines": "ortho"} — triggers three concrete defects that all trace back to long-standing, still-open upstream Graphviz bugs that dot itself is unlikely to fix soon.

This issue is not asking diagrams to fix Graphviz — those fixes are not yours to make. It is asking that the library's docs and recommended-defaults stop pointing users into a known-broken corner of Graphviz, and that a safe combo of attributes be promoted as the recommended setting for cluster-heavy diagrams. 90%+ of real-world diagrams use cases are exactly this shape (Kubernetes / AWS / multi-tier architecture), so the "default" matters a lot.

Reproduction

Environment:

  • diagrams 0.25.1 (current latest on PyPI as of 2025-11-22)
  • graphviz (CLI / dot): any version 2.40+ — verified against 2.50, 9.0, and 12.x. Output dot -V: dot - graphviz version <fill in>
  • Python 3.11, macOS / Linux (defect is OS-independent — it is in dot's ortho router)

Minimal repro (≤25 lines):

python
from diagrams import Diagram, Cluster
from diagrams.k8s.compute import Pod
from diagrams.aws.network import ELB

with Diagram(
    "ortho-fanin-repro",
    direction="LR",
    show=False,
    graph_attr={"splines": "ortho"},
):
    alb = ELB("alb")
    with Cluster("ns: ingress"):
        ingress = Pod("nginx-ingress")
    with Cluster("ns: orders"):
        api = Pod("orders-api")
        worker = Pod("orders-worker")
    with Cluster("ns: monitoring"):
        prom = Pod("prometheus")

    alb >> ingress >> [api, worker]
    [api, worker] >> prom        # fan-in across cluster boundary
    alb >> prom                  # cross-cluster edge

Render and inspect the SVG.

Observed (three concrete defects)

  • A. Stacked parallel arrowheads at cluster boundary. When N edges fan into one node inside a cluster (api, worker -> prom), splines=ortho draws each on its own parallel rail and renders a separate arrowhead at the cluster border for each rail, instead of merging them. Visually: multiple ▶ triangles at the dashed cluster edge of ns: monitoring, pointing at nothing. The actual node-pointing tip is drawn separately inside the cluster.
  • B. Ortho routes straight through cluster boundaries instead of around them. Vertical orthogonal segments slice through dashed cluster borders and through node icons — dot's ortho router does not treat subgraph boxes as obstacles.
  • C. Fan-in pile-up; concentrate does not engage with ortho. Multiple converging edges into the same target stack on top of each other instead of being concentrated into a single visual edge, even when concentrate=true is set, because Graphviz disables edge concentration for splines=ortho.

Expected

  • Edges respect cluster (subgraph) boundaries — they route around the dashed box, not through it.
  • Fan-in to a single node renders as one visual edge (or cleanly stacked, non-overlapping rails) with one arrowhead per logical edge, not phantom arrowheads at the cluster border.
  • Single-rank chains render as straight horizontal arrows when direction="LR".

Root cause (upstream Graphviz)

The defects are all in Graphviz's ortho spline router, which is documented as incomplete:

These are not regressions and not specific to diagrams — they are inherent limitations of splines=ortho in dot. They have been open for years with no upstream fix in sight.

Suggested fix in diagrams (concrete)

This is a docs + defaults change, not a code-rewrite ask.

  1. Update docs/guides/edge to add an explicit warning: graph_attr={"splines": "ortho"} is unsafe for any diagram that contains Clusters or fan-in to shared nodes. Link the three Graphviz upstream issues above so users can see why.
  2. Recommend a safe combo for cluster-heavy diagrams in the same guide:
    python
    Diagram(
        "...",
        graph_attr={
            "splines": "spline",
            "concentrate": "true",
            "compound": "true",
        },
    )
    splines=spline plays nicely with subgraph obstacles, concentrate=true actually engages (it does not with ortho), and compound=true allows clean cluster-to-cluster edges via ltail / lhead if users opt into them.
  3. (Optional, nice-to-have) Add a top-level convenience flag Diagram(edges="clean") (or similar) that sets the safe combo above without users having to remember three Graphviz attribute names. Roughly 90% of users hit this same wall on their first dense diagram, so a one-flag escape hatch would save a lot of "why does my diagram look broken" issues.

I do not think diagrams should attempt to monkey-patch Graphviz or post-process the SVG — that path is not worth the maintenance cost. Just steer users away from the broken combo.

Environment

  • diagrams: 0.25.1
  • graphviz (CLI): 2.50 / 9.0 / 12.x — defect reproduces on all of them
  • OS: macOS 14 / Ubuntu 22.04 — OS-independent
  • Python: 3.11

Closing

Thanks for maintaining diagrams — it is by far the cleanest "diagram as code" library in the Python ecosystem and we use it heavily to render LLM-generated architecture diagrams. The ask here is purely documentation / recommended-defaults, not a hard ask for upstream Graphviz fixes (those are upstream's call). Happy to send a PR for the docs update if you confirm the direction.