· 3 min read ·

The Invisible Graph: Why Nobody Really Knows What Depends on What

Source: lobsters

Daniel Stenberg — the person behind curl, one of the most widely embedded pieces of software on the planet — has a unique vantage point on dependency tracking. When your library ships inside billions of devices, firmware images, Docker containers, and enterprise software stacks, you learn quickly that the dependency graph is not a neat tree. It’s a fog.

His post lands on something I think about a lot in my own projects: we talk about dependency management like it’s a solved problem, and it isn’t.

The Problem Has Two Sides

Most developers think of dependency tracking from the consumer side — you have a package.json or Cargo.toml, you know what you depend on, and tools like npm audit or cargo deny help you reason about it. That part, while imperfect, is at least tractable.

The harder problem is the other direction: if you are the dependency, who depends on you? And at what version? And did they vendor your code, fork it, or statically link it into a binary that got baked into firmware three years ago and will never be updated?

For something like curl, this is essentially unknowable. But even at a much smaller scale — I’ve run into this with Discord bot libraries and small utility packages — you publish something, people use it in ways you never anticipated, and you have no visibility into the blast radius of a breaking change or a security fix.

Why Tooling Hasn’t Solved This

There’s been real progress on the supply chain security front. SBOMs (Software Bill of Materials) are getting traction, especially in regulated industries. OSV and similar vulnerability databases have improved. Tools like Syft, Grype, and Trivy can scan artifacts and generate dependency manifests.

But these tools mostly help the consumer side. They help you know what’s in your container image. They don’t help a library author understand who’s running their code unpatched.

The fundamental issue is that there’s no mandatory registry. You can publish to npm, PyPI, or crates.io and get some downstream signal through download counts and dependents lists — but that’s the tip of the iceberg. Vendored code, private forks, mirrored registries, and statically compiled binaries leave no trail.

What This Means in Practice

When a CVE drops for a widely-used library, the race to patch isn’t just about the library itself — it’s about finding every place it’s been embedded. We’ve seen this play out badly with Log4Shell, with XZ Utils, with OpenSSL vulnerabilities. Each time, the story is the same: the fix ships fast, but the long tail of affected systems takes months or years to clear.

For my own work, I’ve started being more deliberate about a few things:

  • Preferring dependencies with active maintainers and clear security policies over abandoned-but-functional packages
  • Keeping dependency counts low — every transitive dependency is a bet on someone else’s maintenance discipline
  • Watching for vendored copies in my own projects, because they don’t get updated automatically

None of that solves the systemic problem Stenberg is describing. But it’s the best an individual developer can do while the infrastructure catches up.

The Uncomfortable Truth

The software industry has been building on invisible foundations for decades. We’re only now, slowly, starting to map them. Stenberg’s post is a useful reminder that even when you care deeply about this stuff — and few people have thought about it harder than him — the full picture remains out of reach.

SBOMs are a start. Better tooling is a start. But the dependency graph of all the world’s software is genuinely enormous, partially unknowable, and will remain that way for a long time.

Was this interesting?