Technology

The Dependency Cliff: How a Single Unmaintained npm Package Could Take Down Half the Web

As JavaScript projects average over 1,000 dependencies, a growing movement of developers is questioning whether the convenience of npm is worth the existential supply-chain risk—and rebuilding from scratch.

A towering, precarious stack of translucent building blocks rendered in soft gradient lighting, with the bottom block beginning to fracture and glow

In March 2016, a developer named Azer Koçulu deleted a tiny JavaScript package called left-pad from the npm registry. The package was 11 lines of code. It did one thing: it padded the left side of a string with spaces. Thousands of projects—including React, Babel, and a sprawling constellation of build tools depended on it transitively, meaning the developers of those projects often had no idea it was there. Within hours, builds broke across the internet. Facebook’s infrastructure team was fielding questions. Package managers scrambled. A routine npm install became a coin flip.

That was eight years ago. The ecosystem has only grown denser since.

The average JavaScript project today pulls in over 1,000 dependencies when you count the transitive ones—the dependencies of your dependencies, and their dependencies, spiraling down through layers most developers will never inspect. A modern React app scaffolded with Vite might directly list 30 packages in its package.json, but the final node_modules directory balloons to hundreds of megabytes. You trust all of it. You have to. There’s no other way to ship software at the speed the industry demands.

But a growing coalition of developers, security researchers, and infrastructure engineers is starting to ask whether that trust is a liability we can no longer afford—and whether the convenience that made npm ubiquitous is the same convenience that could, one bad day, take down a meaningful chunk of the web.

The Supply Chain Is the Product

The npm registry hosts over 2.1 million packages. It’s the largest software registry in the world by a wide margin. The elegance of the system is also its horror: any developer can publish anything, and any project can depend on anything, and the registry imposes virtually no friction in either direction.

This has produced an ecosystem of extraordinary productivity. Need to format dates? There’s a package. Need to pluralize words? There’s a package. Need to check if a number is even? There’s a package—several, in fact, with slightly different APIs and varying degrees of abandonment.

The problem isn’t the trivial packages themselves. It’s the transitive dependency chains they create. You don’t install left-pad directly. You install a router that uses a formatter that uses a string utility that uses left-pad. When that bottom-of-the-stack maintainer yanks their code, the entire tower wobbles.

And they don’t just delete packages. Sometimes they compromise them.

A Brief History of Going Sideways

The left-pad incident was the wake-up call that prompted no real structural change. Since then, the supply chain attacks have grown more deliberate:

  • event-stream (2018): A popular streaming library was handed off to a new maintainer who injected code targeting a specific cryptocurrency wallet application. It went undetected for months.
  • ua-parser-js (2021): The maintainer’s npm credentials were hijacked, and three malicious versions were published containing credential-stealing trojans and cryptominers. The package had millions of weekly downloads.
  • node-ipc (2022): A maintainer deliberately sabotaged their own package to protest the war in Ukraine, shipping an update that wiped files on machines with Russian and Belarusian IP addresses. The intent was political. The mechanism was a dependency chain that pulled node-ipc into projects whose owners had no stake in the conflict.
  • colors.js and faker.js (2022): Developer Marak Squires intentionally pushed infinite-loop code to colors.js, breaking thousands of pipelines, and later sabotaged faker.js in a dispute over unpaid corporate use of his open-source work.

Each of these incidents is a case study in a single point of failure. But together, they describe a pattern: the npm ecosystem has built a system where millions of developers are effectively delegating trust to hundreds of anonymous maintainers, many of whom are unpaid, unsupported, and one bad day away from making a decision that ripples across the network.

“We’ve built a supply chain where the most critical infrastructure is maintained by people we’ve never met, compensated by nobody, and audited by no one with the authority to intervene. It’s not a security model. It’s a hope model.”

That observation, from Feross Aboukhadijeh, the creator of Socket—a tool that attempts to detect suspicious package behavior—captures the essential absurdity. We don’t have a dependency problem because dependencies are inherently bad. We have a dependency problem because we’ve built our critical infrastructure on a model that assumes good faith at a scale where good faith is statistically insufficient.

The Rippers and the Reducers

The response to this risk is bifurcating into two camps.

The first camp is the reducers: developers and teams auditing their dependency trees, cutting unnecessary packages, and adopting stricter policies around what gets installed. Tools like Socket, Snyk, and GitHub’s Dependabot have made it easier to monitor for vulnerabilities and suspicious updates. Some companies now require internal review of any new dependency above a certain download threshold. The philosophy is pragmatic: you can’t eliminate dependencies, but you can shrink the attack surface.

The second camp is more radical. These are the developers questioning whether the entire paradigm needs rethinking. They point to alternative runtimes like Deno, which ships a standard library and discourages the sprawling dependency culture that defined Node.js. They point to Bun, which is trying to reduce friction in a different direction—speed—but still operates within the npm world. They point to the resurgence of interest in writing code rather than installing it: the idea that a 200-line utility function in your own codebase, maintained by people you work with, is more trustworthy than a one-line package maintained by someone you can’t reach.

There’s also a quieter movement toward vendoring—copying dependency code directly into your repository rather than fetching it at build time. It’s the way Go handles many of its dependencies, and it trades convenience for control. You know exactly what’s in your codebase because it’s in your codebase. You don’t need to trust the registry. You don’t need to trust the maintainer not to push a malicious update at 2 a.m. You just need to trust the version you copied, and you can audit it once.

Vendoring sounds archaic. It is, a little. But in a world where the marginal cost of a compromised supply chain is measured in millions of dollars and weeks of incident response, archaic starts to look rational.

The Cliff Is Structural

The uncomfortable truth is that npm’s dominance created a coordination problem that no single actor can solve. The registry is owned by GitHub, which is owned by Microsoft. The packages are maintained by individuals. The consumers are everyone. There is no governing body with the authority to enforce maintainer identity verification, require multi-factor authentication for publishers of critical packages, or mandate that packages above a certain download threshold have more than one maintainer.

The npm team has made incremental improvements—two-factor authentication for high-impact packages, for instance. But the fundamental architecture remains: a publish-first, review-later system running at internet scale. Every npm install is a small act of faith. Every node_modules directory is a microcosm of trust, most of it inherited, little of it earned.

The developers rebuilding from scratch aren’t doing so because they’re paranoid. They’re doing it because the math has changed. When your project has 1,000 dependencies, the probability that at least one of them is maintained by someone who is burned out, bitter, compromised, or simply careless approaches certainty. Not possibility—certainty.

The question isn’t whether another left-pad will happen. It’s whether the next one will be 11 lines of missing string-padding, or something quieter, smarter, and much harder to detect. The cliff is right there. We’ve just been too productive to notice how close the edge is.