Skip to main content
postmortem

What the Review Missed and What Changed

5 min read Chapter 34 of 38

What the Review Missed

npm Inc. published a blog post addressing the incident. The immediate response was to establish a new policy: packages with more than a certain number of dependents (other packages that depend on them) cannot be unpublished. Once a package becomes shared infrastructure, the author cannot unilaterally remove it from the ecosystem. The policy also introduced a time window: packages can be unpublished within 72 hours of publication, but not after.

This policy is pragmatic and addresses the immediate trigger: a maintainer unilaterally removing a package that thousands of projects depend on. But it raises a question the blog post does not fully address: what obligations does the ecosystem impose on maintainers, and what support does the ecosystem provide in return?

Koçulu’s complaint was legitimate. npm Inc. transferred his package name to a corporation without his consent. His response, unpublishing all his packages, was disproportionate in its impact but proportionate to his grievance. The incident exposed a tension in open source package ecosystems: maintainers donate their labor to the ecosystem, the ecosystem becomes dependent on their work, and then the ecosystem constrains the maintainer’s ability to withdraw that work. The maintainer receives no compensation, no contractual obligation, and no formal authority, but is expected to maintain the infrastructure indefinitely.

The review did not address the deeper structural issue: why does the JavaScript ecosystem depend on 11-line packages in the first place? The left-pad function is trivially implementable in any project. It is a for loop with string concatenation. The decision to import it as a dependency rather than write it is a tradeoff: reduced code duplication in exchange for increased dependency graph complexity. For packages that encapsulate substantial logic (cryptography, HTTP parsing, date manipulation), the tradeoff is clearly favorable. For packages that contain a single trivial function, the tradeoff is questionable. The overhead of the dependency (trust, availability, version management, transitive dependencies of the dependency itself) exceeds the cost of writing the function.

What Changed

npm unpublish policy. The policy that packages with dependents cannot be unpublished is now standard. This prevents the exact scenario that caused the left-pad outage: a maintainer removing a widely-depended-upon package. The tradeoff is that maintainers cannot fully withdraw their work once the ecosystem depends on it.

Dependency awareness. The left-pad incident created widespread awareness of transitive dependency risk, not just in JavaScript but across all package ecosystems. The incident became the standard reference for:

Why you should understand your full dependency tree, not just your direct dependencies.

Why dependency count matters as a risk metric. A project with 1,500 transitive dependencies has a larger attack surface and a higher availability risk than a project with 50.

Why trivial dependencies (packages that implement one simple function) are not worth the structural cost of the dependency. The cost of writing leftpad yourself is five minutes and zero new dependencies. The cost of importing it is a new node in your dependency graph, a new trust relationship, and a new availability dependency.

Lock files as standard practice. Before left-pad, lock files were a tool that some projects used. After left-pad, lock files became standard practice in every major package ecosystem. npm introduced package-lock.json in npm 5 (May 2017). Yarn had already introduced yarn.lock in October 2016. The practice of committing lock files to version control, ensuring that every developer and every CI system builds against the same resolved dependency tree, became the expected workflow.

Registry resilience. npm and other package registries invested in infrastructure to prevent single points of failure. This includes read-only mirrors, CDN distribution, and offline caching tools. The npm CLI added local caching behavior that reduces (but does not eliminate) the dependency on the live registry for repeat installs.

Supply chain security awareness. The left-pad incident was the opening chapter of a broader industry reckoning with dependency supply chain risk. Left-pad was an availability incident: a package disappeared and builds broke. The subsequent years brought security incidents: malicious packages uploaded to npm, typosquatting attacks, compromised maintainer accounts, and dependency confusion attacks. Left-pad demonstrated that the ecosystem was fragile. The security incidents demonstrated that it was also vulnerable. The combination drove the development of tools like npm audit, Socket, Snyk, and the OpenSSF Scorecard project, all aimed at assessing and improving the security and reliability of the open source supply chain.

The Rule

Do not depend on trivial packages for functionality you can implement in fewer lines than the dependency declaration requires. Every dependency is a trust relationship, an availability dependency, and a node in your attack surface. The cost of a dependency is not the lines of code you save. It is the infrastructure you rely on.

This rule comes from left-pad, where 11 lines of string padding code, published as an npm package, became a transitive dependency of thousands of projects, and when the author removed it, the JavaScript ecosystem’s build infrastructure failed for 2.5 hours.