#Cybersecurity

Against Convenience: The Case for Vendoring Every Dependency

Tech Essays Reporter
8 min read

A provocative wiki post argues that the simplest defense against supply chain attacks isn't better policy or slower update cadences, but a return to an older habit: copying every dependency directly into your repository and committing it. The argument is less about disk space than about restoring friction to a process we made too cheap.

The thesis arrives wrapped in a confession that the title is clickbait, but the argument underneath (ReuseLessSoftware, updated June 2026) is serious and unusually clear-eyed about its own history. Its claim is that the modern epidemic of software supply chain attacks is not primarily a security problem in the technical sense. It is a consequence of an economic one. We made the distribution of software so cheap that we automated it everywhere, and automation, once it exists, gets used even where it is wasteful. The remedy proposed is almost embarrassingly low-tech: stop fetching your dependencies fresh on every build, and instead vendor them, copy the upstream source into your own repository and commit it like you mean it.

What makes the piece worth reading is not the recommendation itself, which is old and familiar to anyone who has worked in a large monorepo, but the way it situates that recommendation inside fifty years of changing cost structures. The author is doing something a working programmer rarely bothers to do, which is to ask why the current default exists at all, and whether the conditions that produced it still hold.

The historical argument

The story begins with the original software crisis of the late 1960s and early 1970s, when demand for software grew exponentially while our ability to produce reusable, composable software grew sub-linearly. The research response gave us modularity and structured programming, and those ideas succeeded so thoroughly that they became invisible. As the author puts it, nearly every module system designed since 1990 traces its lineage to Modula-2, and we simply stopped thinking about the problem because it had been solved well enough.

The internet then built a second, more powerful answer. If most software you actually want is open source, and if building and shipping it has become nearly free, then you can hand the ability to distribute source code to anyone who wants it and let them build it themselves. The vendor that once performed all the laborious integration work became optional. On the foundations of CPAN, CTAN, and Linux distributions, we grew an entire ecosystem of language-specific package managers, tools that take a manifest and a version number and automatically resolve, fetch, and build whatever a project names as a dependency.

The author is careful, almost insistent, about not romanticizing the era this replaced. Spending days of one's life trying to get SDL to build with all its features in 2003 was genuinely miserable, and the warning is unambiguous: do not pine for those days. The point is that the automation solved a real and painful problem. The trouble is that it solved it so completely that we now suffer from the opposite condition. Where the 1970s suffered from too little reuse, we suffer from too much, and the costs show up as dependency hell, bloat, build times, vanishing packages, and abandoned maintainers.

Why supply chain attacks scale now

Supply chain attacks are not new. The piece reaches for a canonical example, the 2003 attempt to backdoor the Linux kernel by slipping uid = 0 where uid == 0 belonged, a single character that would have granted root and which was notable mostly for being the first malicious kernel patch caught in the wild. What has changed is not the existence of the attack but its propagation speed. Continuous integration systems run on nearly every change, and a compromised dependency becomes available to every downstream consumer the moment it lands, whose own CI then ingests and rebuilds it. A well-constructed attack spreads as fast as the runners can execute, which is to say almost instantly and almost everywhere. The recent npm and PyPI incidents are not aberrations so much as the predictable behavior of a system optimized for frictionless propagation.

One school of thought proposes to fight this by reintroducing delay, through dependency cooldowns or quarantine periods that give the community time to notice malicious releases before they propagate. The author acknowledges the idea but notes its weakness, which is that it immediately collapses into an argument about policy and about who volunteers to be the guinea pig absorbing the risk during the cooldown window. The vendoring proposal sidesteps that negotiation entirely by relocating the decision to each individual project.

The mechanics, and the side effect that matters more

The technical proposal is simple to the point of being anticlimactic. Do not pull dependencies from a network location on every build. Commit them into source control alongside your own code. When upstream updates, fetch it deliberately and copy it again. If doing this by hand becomes tedious, that is precisely the job your build tool should automate, with the lockfile correlating to a full source tree you actually possess. The objections mostly dissolve on inspection. The repository grows, but disk is cheap. Build times suffer, except they do not, because you were compiling that code anyway. Code sharing between a client and server that speak a common protocol becomes harder, but those programs already face version-mismatch problems and are better served by confronting them honestly than by pretending a shared dependency makes them disappear.

The consequence the author cares about most is structural. By refusing automatic updates, every package in an ecosystem becomes a firebreak. A compromised upstream release no longer flows automatically into everything downstream, because nothing downstream updates without a human choosing to. This same firebreak does slow the propagation of legitimate bug fixes, and the author is honest about it, arguing that the fixes which genuinely matter are ones you would hunt for deliberately, while the ones you never go looking for usually did not matter to you in the first place.

But the part of the essay that lingers is the claim about friction as a feature rather than a defect. Vendoring slightly raises the cost of taking on a dependency, and that small additional cost forces a moment of deliberation: do you really need this, really? It also raises visibility. A library you expected to be two hundred lines, sitting expanded in your tree at fifty thousand, announces its own bloat in a way that a single line in a manifest file never will. The magic that hides what a dependency drags in is exactly the magic that makes a transitive dependency a quiet transfer of control over your own program to someone you have never met. The proposal's deepest point is philosophical rather than operational. The cheapness of reuse anesthetized us to its costs, and reintroducing a little friction restores the judgment that the friction used to enforce for free.

Where it breaks down

The analysis does not overreach, which is part of why it persuades. The author concedes that not everything can be vendored. Bundling all of Redis into a web backend deployment is unreasonable, and at some point dependencies meet the operating system, which is a vast and troublesome dependency of its own. There is a complexity ceiling, though the giant monorepos at Google and elsewhere suggest that ceiling sits far higher than most engineers assume. The genuinely hard cost is deduplication: when library A and library B both depend on Z, vendoring makes sharing that common dependency harder rather than automatic. The author's reply is that shared transitive dependencies were always a source of trouble too, and that the difficulty is the whole point being surfaced rather than a bug being introduced.

The essay also gestures at where the principle leads if followed all the way down. Take it to its conclusion and you arrive at Nix or Guix, systems that treat the entire build environment as something to be specified completely rather than assembled by convention out of whatever happens to be installed. The notion of a loosely defined build environment, the author suggests, is a lazy relic of an age when software was compiled once on some minicomputer and then distributed as binaries, an assumption that no longer matches a world where we rebuild software constantly and on the fly.

What it asks of us

The author signs off self-deprecatingly, as an idiot with an opinion inviting discussion, which undersells the contribution. The piece is a useful reframing of a security problem as an economics problem, and economics problems are usually solved by changing incentives rather than by exhortation. Cooldowns and provenance tooling and signed releases all try to make the automated pipeline safer without questioning whether so much of it should be automated in the first place. Vendoring questions exactly that. It does not eliminate supply chain risk; a malicious release you deliberately vendor is still malicious. What it changes is the default, shifting the act of incorporating someone else's code from something that happens invisibly and continuously to something that happens because a person decided it should. Whether the broader ecosystem will trade a measure of convenience for that deliberation is genuinely uncertain, since the convenience is the very thing that made these tools win in the first place. But the argument that we overcorrected, that we solved the build problem so well we forgot it was ever protecting us from anything, deserves to be taken seriously by anyone who has watched a single compromised package ripple through half the registry in an afternoon.

Comments

Loading comments...