Modern software is a house of cards built on open source dependencies. Every major language has a public registry of packages developers can pull into their projects.

NPM, the registry for JavaScript and TypeScript, hosts over 3 million public packages. Crates.io, Rust’s registry, has fewer, around 200 thousand, and is growing quickly.

This is mostly a good thing. It means we can write complex logic once, review and audit it, and reuse it everywhere. I can pull a dependency that helps with testing, handles data encoding, provides cryptographic primitives I don’t want to reimplement, or even manages an entire authentication layer for a web app.

The result is obvious: faster development, quicker shipping, and the ability to stand on the shoulders of a global developer community. When used well, dependencies give you leverage - battle-tested code, shared maintenance, and fewer wheels to reinvent.

But it also makes us lazy. Instead of writing a few lines of code, we reach straight for third-party packages. Some libraries with millions of weekly downloads could be replaced by a handful of native lines.

Then comes the hard part: managing it all. Engineers are meant to keep dependencies current, verify they’re safe, remove what’s outdated or unused, and document why each one exists.

In practice, that almost never happens. It’s easy to add a dependency, but hard to remove it. Once it’s in, it tends to stay forever.

With AI vibe coding, this will only get worse. Agents automatically install packages to achieve a user’s goal. Users may not even realize what’s been added, or what security implications it brings.

Supply-chain attacks are real. We frequently see malicious packages slipping into production systems. In just the last 12 months, we have seen incidents like the ByBit hack which resulted in ~$1.4 billion of stolen assets. More recently, a popular developer qix was compromised leading to malicious versions being published for dozens of packages, including chalk, strip-ansi, and color-convert.

The affected packages were downloaded over 1 billion times. Luckily, the impact was quickly detected and the attack surface was minimised. These packages are used ubiquitously in the JS & TS ecosystem meaning a large portion of applications would have been affected. The full list of recent compromises is long and growing, see this excellent report on open source supply-chain attacks from 2024 & 2025.

Existing tools like Dependabot, npm audit, or cargo audit all help detect outdated or vulnerable packages, but they assume the dependency should be there in the first place. They don’t ask the more fundamental question: should this dependency exist at all for a given application and what is its purpose?

That’s the gap: Formal and structured dependency documentation with linting.

Most projects have no record of why a dependency exists at all in a codebase.

That’s why we built Dep Doc - a linting tool that enforces justification and documentation for every dependency in your project and makes their purpose and use explicit.

It checks that every package in your project has corresponding documentation, and fails linting if anything is missing, unused, or incorrectly scoped. It works with both package.json and Cargo.toml projects.

You can run it locally or in CI. It’s lightweight and easy to integrate.

See it in action in its own CI release flow here.

You can find the repo and documentation here.

It’s available, ironically, as a third-party dependency itself here.

PRs and contributions welcome.