2021/10/25

19

Given all of the recent fun with ua-parser-js , package managers have been on my mind. In a design class in college, I was introduced to TRIZ . TRIZ is known in English as "The Theory of Inventive Problem Solving" and was born in the Soviet Union. The most valuable aspect of this methodology is the idea of looking at the absolute worst possible design for the final system. A phone that explodes, a plane that can't fly, or a vacuum that can't suck up dirt are the kinds of inventions drummed up during these exercises. Let's create the worst package manager possible!


To start, it would be great if the package manager was serving a language with no standard library and that must run on every type computer that exists. This ensures that there are edge-cases and bugs aplenty which means that we will "need" a whole bunch of packages to accomplish simple things. (Bonus points if the language is interpreted and dynamically typed).

It would be awesome if the package manager couldn't detect vulnerabilities in packaged code. And, it'd be even better if it reported completely safe code as dangerous .

Complete mayhem would be caused if the package manager sometimes decided to install different code than what was requested. Or, we could allow anybody to edit any package and republish whenever they like. These two combined would guarantee that a user has absolutely no idea what code they are bringing in. If our community decided to treat more dependencies as though they don't have a cost, we could start to see absurdly large dependency trees that further exacerbate the unknown code issue.

Allow packages to define arbitrary code to be executed at install-time so that a publisher can run whatever code they want on user's machines.

We could use a convoluted system for versioning: out with Semantic and in with Pseudorandom Versioning! Every time the package is published, pick a random 32 bit unsigned int and call that the new version! Provide no notion of version order, making upgrades impossible. Instead, always get the latest version (the only one that is available).

Provide no interface to allow a publisher to take down specific package versions and broadcast potential vulnerabilities to downstream dependents.

A little cherry on top would be if the tooling for interfacing with the package manager was written poorly, sometimes crashing and often eating up computational resources for seemingly no reason. We could even slap a crypto miner in there to make some money!

Why stop there? Why not starting mining all of a user's local data on their machine? Surely somebody would pay good money for it!


While this is all intentionally hyperbolic, it is somewhat grounded and targeted at npm. I am not aiming to paint npm as the worst possible version of a package manager that could ever be written; rather I want to share the areas where there is an alarming amount of overlap in my view. npm has provided great value to a whole ecosystem of developers and by extension, their users/stakeholders. I think there is a lot to be learned from the current state of npm and what steps can be taken to make it a better package manager. Even if it does not last, we can take these lessons and use them to build a better package manager. I wouldn't be bold enough to assume that I can come up with the list of things a new package manager must get right in order to be great (especially without much thought), but I can easily spot the things not to do.

My most immediate concern is the ability for packages to execute arbitrary code on install. I don't see how this needs to be a feature, and I can't drum up any convincing argument in favor of it. A package manager should allow a developer to grab a bundle of code that they can then use as they wish.

There is a happy middle ground between package size and the average number of dependencies. As of now, most packages are tiny which allows them to be easily reviewed and audited. However, this pushes the complexity onto the dependency tree itself. It is near impossible to review thousands of nested dependencies by hand. At the same time, one monolithic library is subject to the whims of whoever controls it. There is no complex tree to look through, but now there are tens (if not hundreds) of thousands of lines of code to review. Somewhere in the middle there is room for moderately sized packages with a handful of dependencies that are also similarly sized. Both the packages and dependency tree can be reviewed in a reasonable amount of time and trusted.

I think everyone who uses npm would agree that this could all be much better, but I don't see how we get there.