Skip to content

CONCEPT Cited by 1 source

Transitive-dependency reachability

Transitive-dependency reachability is the graph-theoretic reason a package's cost depends on how it's imported: importing a package transitively brings in every package it imports (that isn't excluded by build constraints), plus the runtime.

Formal shape

  • Nodes: Go (or language-X) packages.
  • Edges: direct imports, filtered by build tags / GOOS / GOARCH / preprocessor conditions.
  • Reachable set from package M: the transitive closure of outgoing edges from M.
  • Binary includes: every package in the reachable set from main, plus the linker's view of which symbols in each package are reachable (subject to DCE).

Why it matters operationally

A single directly-imported package with one edge to a heavy dep can drag the whole heavy dep into the binary. Worse, the edge can be hidden inside a shared utility package that multiple binaries use for unrelated reasons.

Canonical wiki instance (Datadog 2026-02-18)

Datadog Agent trace-agent binary:

  • go list showed 526 k8s.io/* packages included.
  • systems/go-size-analyzer attributed ≥30 MiB to them.
  • systems/goda's reach function traced the entire k8s import graph back to one function in one package of the Agent codebase. The function itself had no Kubernetes dependency; the package it lived in did.

Fix: move the function into its own package (patterns/single-function-forced-package-split). Binaries that only needed the one function stopped importing the original package and its heavy dep cluster.

Result: 570 packages removed, 36 MiB binary-size cut — "more than half of the binary".

Tools to reason about it

  • go list -f '{{ .Deps }}' — what's included.
  • systems/goda reach(main, target) — the import paths from main to any target package.
  • systems/go-size-analyzer — byte cost per package (where DCE interacts with reachability).

Design implications

  • Shared utility packages are amplifiers. A single heavy import in a package used widely turns a localized cost into a global one.
  • Audit edges not packages. The binary-size engineering question isn't "is this package expensive?" but "is there an edge whose removal would drop a big subgraph?"
  • One-edge fixes are common. The 2026-02-18 post explicitly frames the k8s case as "an extreme example, but not a unique one. We found many similar cases."

Generalises beyond Go

C/C++ include graphs, Python import side effects, npm package.json direct + transitive deps, JVM classpath reachability — same graph model, different language-specific edges + pruning mechanisms.

Seen in

Last updated · 200 distilled / 1,178 read