When Azer Koçulu unpublished left-pad, the code barely mattered. The graph it sat in did.

One small screw lying alone on a vast pale floor, shot from standing height
Eleven lines, and thousands of builds In March 2016 an eleven-line package was unpublished and broke builds across the ecosystem. The code was trivial; the dependency graph was not.

A package so small it should not have mattered

Left-pad did one thing: it took a string and padded its left side with a character — spaces by default — until it reached a given length. The whole implementation was eleven lines of JavaScript. Any developer could have written it in ten minutes from memory, and many had. It existed as a package on npm not because the task was hard but because npm made publishing trivially easy and because the convention of the ecosystem encouraged it: small, composable, single-purpose modules were the fashion of the moment, and the tooling rewarded that fashion by making installation a single command.

Azer Koçulu had published left-pad in 2014 as part of a larger collection of small utilities. The package accumulated dependents quietly, invisibly, the way things did on npm. Babel depended on it. React's own development tooling depended on it. Those dependencies pulled it into thousands of other projects, whose maintainers had no particular awareness that a string-padding utility stood somewhere in their graph. The dependency graph — the tree of packages that a project requires, directly or transitively — had grown to include left-pad without anyone having made a deliberate choice to trust Azer Koçulu with their build.

Lifted from the piece

The shape of the incident

  1. Left-pad published2014, Azer Koçulu
  2. Kik naming disputeearly March 2016
  3. Unpublish and mass breakage22 March 2016
  4. npm restores packagesame day, hours later
  5. npm unpublish policy changedweeks following; later formalised

The argument and the unpublishing

The immediate cause was a naming dispute. Koçulu had also published a package called kik. The company Kik Interactive — makers of the messaging application of the same name — asked npm to transfer the name to them, citing the company's prior claim to it. npm complied, transferring the name without Koçulu's agreement. He responded by unpublishing every package he had published, left-pad among them. From his perspective, the principle mattered more than the inconvenience: a registry that transferred names against a maintainer's wishes was not a registry he wanted to support.

npm's intervention was not capricious. The company's evolving policies had always acknowledged that a name with obvious prior commercial use might be claimed, and Kik had argued that case. But the outcome exposed something nobody had stress-tested: npm's unpublish mechanism had no circuit-breaker for packages with large downstream dependency counts. In March 2016, unpublishing was something an author could do unilaterally, immediately, for any package under any version. The registry had no concept of a package being too embedded in the graph to remove safely.

When left-pad disappeared from the registry, npm's resolution process had nothing to serve. Builds across the ecosystem broke within hours. Continuous integration pipelines failed. Companies whose engineers had never heard of left-pad found their deployments halted. The breakage was not dramatic in the sense of data loss or security compromise — it was mundane in a way that made it worse: a string-padding function was unavailable, and nothing could ship.

npm logo in white lowercase letters on a red rectangular background
A dependency became free to add and expensive to audit npm launched in 2010 and made installing someone else's code a single command. The cost moved from adding it to knowing what you had added.Photo: Npm-logo · Wikimedia Commons
Lifted from the piece

What the policy shift settled (and didn't)

  • What it settled: authors cannot unilaterally remove a widely-depended-on package version
  • What it didn't settle: who owns a package name when a commercial entity wants it
  • What it left open: the question of maintainer compensation for packages that become infrastructure

What the graph actually rested on

The left-pad incident made visible what the dependency graph had always been: a social arrangement as much as a technical one. The graph assumed that published packages stayed published. It assumed that maintainers would keep their names. It assumed that the registry would remain neutral among maintainers. None of those assumptions had been written into a contract; they were conventions that had hardened into infrastructure, and when one broke, the weight of the ecosystem fell through the gap.

Semantic versioning, the system npm used to express compatibility — major version for breaking changes, minor for new features, patch for fixes — had always contained a hidden dependency on registry stability. A ^ prefix in a package.json file says "give me any compatible minor or patch update". It says nothing about what to do if the package ceases to exist. The caret operator presumed the presence of something to resolve against.

The code itself was genuinely trivial. Several developers pointed out, during the chaos, that the function's implementation was simple enough to vendor — to copy directly into a project and stop depending on the registry for it. A few lines in a utilities file, no npm involved. That was true. It was also a description of exactly what the culture of small modules had discouraged for years. The ergonomics of npm install had made vendoring feel archaic, and thousands of projects had arrived at a state where a utility they could have written themselves was instead an external dependency whose continued existence they had never thought to verify.

Empty chair facing away in a bare meeting room with half-shut blinds
A maintainer handed a popular package to a stranger The 2018 event-stream incident began with an ordinary handover of an unpaid maintenance burden, which is the part that has not been solved.

The aftermath and the policy that changed

npm restored left-pad within hours — by restoring the unpublished version from the registry itself, preserving the package name and the version numbers so that existing lockfiles would continue to resolve. It was a pragmatic solution that also revealed, implicitly, that npm held more authority over the namespace than individual maintainers might have assumed.

The longer policy change came shortly after. npm introduced a rule preventing the unpublishing of any package version that was older than a certain age or that had significant downstream dependents. The precise thresholds evolved, but the principle was clear: once a package was embedded deeply enough in the graph, it passed a point of no return, and the author's unilateral control over it ended. The registry was no longer simply a host for an author's files; it was infrastructure, and infrastructure had different obligations than a personal file server.

That distinction — between a tool and infrastructure — had never been drawn explicitly before March 2016. It became impossible to avoid afterward. The incident fed into broader conversations about the governance of open-source dependencies, the question of who bears responsibility when a volunteer's decision breaks a corporation's build, and the asymmetry between the ease of adding a dependency and the cost of auditing or removing one. Those conversations continued through later and more serious incidents: malicious code inserted into packages by bad actors who had acquired maintainer access, deliberate sabotage by maintainers who had grown tired of commercial free-riders. Left-pad was not those things. It was an honest disagreement about a name, and a revelation, accidental and complete, of what the build actually rested on.