Readit News logoReadit News
tmcw · 6 years ago
How to write a silly post about JavaScript dependencies:

- Don't mention other languages, lest you reveal that most of them have similar dependency bloat problems.

- Talk about devDependencies and dependencies without considering how they might be entirely different, say… one kind gets bundled, the other doesn't.

- Always use npm, so you can highlight duplicates. Don't use yarn.

- Adopt a narrow definition of 'dependency hell' so you can focus on the thing that JavaScript does imperfectly (minimizing number of dependencies) and avoid talking about the things that it does well (handling conflicting deps, solving diamond deps, resolving dependency chains)

- Don't worry about explaining how any of the issues you're discussing have real-world implications. Have companies been failing because they run out of disk space because of node_modules?

- Take automated security audits extremely seriously. A backtracking bug in the argument parser for the web framework's local CLI tool? Of course it's not exploitable, when… it's a web app, and the CLI tool is something you run locally. But add it to the count as if it is!

rootlocus · 6 years ago
> - Don't mention other languages, lest you reveal that most of them have similar dependency bloat problems.

The post contains a graph with the package counts for other languages [1]

1 https://d33wubrfki0l68.cloudfront.net/7481900a584f733e7e9db7...

danShumway · 6 years ago
These comparisons are so bad.

NPM is hands-down the most popular packaging ecosystem online. So of course it has more packages than other ecosystems, more developers use it. It's not a good comparison.

I would not be surprised to find that JS bloat tends to be more severe than other languages, because Javascript makes it a lot easier to import and publish packages. I would also not be surprised if Node package management ended up being a bit worse, again because Node lowers the barrier of entry for inexperienced programmers to publish packages on impulse, even if they don't plan to maintain them. And its just undeniable that Javascript is a heavy intro language, so the average developer quality (especially around responsible dependency management) is probably less than other languages.

But the package count stats need to just die. When we talk about dependency bloat, useful stats would be:

- What is the average/median number of (deduplicated) dependencies in a Node program vs a Rust/Python/Ruby project?

- What is the average/median number of lines of code in a Node package, minus its dependencies? How does that compare to the average/median package in Rust/Python/Ruby?

- What is the average/median number of outdated dependencies in a Node package, vs the average/median in Rust/Python/Ruby?

- What are the average/median number of different authors across a Node package's dependencies vs Rust/Python/Ruby?

- What percentage of the Node ecosystem actually gets used? Are we looking at a long tail of mostly ignored packages, or is usage fairly spread out and diverse?

Heck, even this mostly useless graph would be better if it just adjusted for the number of users for each platform. It's pretty tough get that data, but there are sources you could look at to try and guess, including StackOverflow's dev surveys[0].

The "how many packages are there" metric means nothing when it's quoted in isolation from other data. It's like claiming Windows developers are vastly more productive than Mac developers because more Windows software exists.

[0]: https://insights.stackoverflow.com/survey/2019#overview

ng12 · 6 years ago
Is node's biggest crime is that it's easy to publish packages? Is this an argument for intentionally complicating package systems?
ng12 · 6 years ago
They also strategically avoid mentioning the payoff which is that your project is entirely self-contained. `node_modules` is at once your complete development kit, build tool, compiler, and collection of runtime dependencies.

Once you check out a node project with a package.json you can reasonably assume you can install it and run it anywhere. A docker image for a node project should just be "install node && npm build". You're side-stepping the entire dev ops nightmare. Find me another language with JavaScript's popularity and that level of simplicity. I'd go as far to argue that this is what happens to any massively popular language that has a functional package manager from day one.

stickfigure · 6 years ago
I'm totally baffled by this statement.

Find me a mainstream language that's more complicated than this? "Checkout and build" works pretty much everywhere? I mean, you need the right version of mvn or pip or whatnot... just like you need the right version of npm.

If anything, node/npm deserves special mention for NOT having reliable repeatable builds. Part of this is that a lot of packages rely on native code. But most of it is that the ecosystem culture defaults to "always grab the latest versions of everything".

And npm only got package-lock.json a few years ago, not "from day one". Prior to package-lock.json, builds were wildly unpredictable - like, expect breaking changes week to week even if you don't change anything at all in your code.

If you want an "entirely self contained" payoff, languages that produce static binaries are pretty hard to beat. Node is not that.

johannes1234321 · 6 years ago
Unless any library you use depends on a native module, then you are in for surprises.
rootlocus · 6 years ago
> A docker image for a node project should just be "install node && npm build"

That defeats the purpose of having separate dev and runtime dependencies and results in a huge docker image filled with dev-time dependencies.

Also, other package managers do this fairly well. Java has maven and uber-jars (for self contained artifacts), python has pip and venv, etc.

seemslegit · 6 years ago
How to downplay the severity of the problem:

Ignore the ridiculously small functionality scope that acceptably qualifies for a js package in a way that does not exist in any other ecosystem and ads a big multiplier in front of all the other risks involved.

jessaustin · 6 years ago
If these claims were true, we would have seen some examples by now. Instead, we get some tired invocations of "leftpad!" along with some more tired FUD. Who has been hacked because they used javascript instead of some other "more mature" language?
dmitrygr · 6 years ago
> - Don't mention other languages, lest you reveal that most of them have similar dependency bloat problems.

no such problems in C, i promise you

dwheeler · 6 years ago
Part of the issue is that JavaScript packages are often far far smaller than packages in other ecosystems.

"small packages are extremely common in the JavaScript npm package system. For example, in npm, 47% of the packages have 0 or 1 functions, and the average npm package has 112 physical lines of code. In contrast, the average Python module in the PyPI repository has 2,232 physical lines of code."

Source: "Vulnerabilities in the Core: Preliminary Report and Census II of Open Source Software", The Linux Foundation & The Laboratory for Innovation Science at Harvard, by Frank Nagle, Jessica Wilkerson, James Dana, Jennifer L. Hoffman. https://www.coreinfrastructure.org/programs/census-program-i...

(This is a contrast of citations from other works; you can see the specific citations in the report, but I'm trying to do this from a phone. In any case, the issue is the massive difference between JavaScript and everything else, and I think this quotation shows it nicely.)

dep_b · 6 years ago
iOS dependencies in Cocoapods are including less than 1 other packages on average (1). The most I see are Alamofire (a networking package people keep using for reasons that I do not fully understand) and Starscream (a Websocket implementation, that's pretty hard to write from scratch to be honest). Having unnecessary dependencies is pretty much frowned upon.

One of the reasons is that the iOS SDK is really complete by itself while Javascript doesn't even ship with a usable Date implementation. If somebody could just come up with a standard library for JavaScript that is really widely adopted I think the amount of packages can be cut back dramatically.

(1) totally based on gut feeling, not based on hard data

hn_throwaway_99 · 6 years ago
Java didn't have a decent date implementation until Java 8.

> If somebody could just come up with a standard library for JavaScript that is really widely adopted I think the amount of packages can be cut back dramatically.

As this article points out, I think that lodash is that for many people.

SahAssar · 6 years ago
What makes the js Date implementation unusable in your view? IMO it works pretty okay for most needs.
inakarmacoma · 6 years ago
Google Apps Script! Heh...
matheusmoreira · 6 years ago
Why are small packages bad? Independent functions should be versioned and distributed independently. Otherwise we get several utility packages which are nothing but collections of independent functions.

Widely used packages should form the basis of a standard library that is distributed with the language itself.

BiteCode_dev · 6 years ago
Small packages means more packages, and more packages compounds the dependency graph which:

- makes dependency resolution harder

- means more points of failure

- makes auditing difficult

- makes install time longer

- leads to harder maintenance and upgrade

This also causes heterogeneity in the mass of your dependencies:

- it splits the resources for documentation, testing, tutorial, etc

- it ensures very weak integration between various building blocks, forcing everyone to rebuild glue code again and again

- it makes discovering the proper solution to your problem harder as you must chose a combination of dependencies instead of one

- it makes contributing to the project harder, especially to junior profiles, and increases the price of on-boarding

- eventually, it leads to a culture that pushes the "libs versus frameworks" so far you never get a decent framework for anything. This is why there is no JS equivalent to RoR or Django.

There is, of course, a balance to reach. You don't want a lib that does everything.

mschuetz · 6 years ago
Every separate package adds overhead and another maintainer that you've got to put your trust in. I'd rather have few packages of well trusted maintainers instead of a thousand packages from god knows who.

Personally, I've made it a habit to not add anything that requires trivial one liner garbage packages, which amounts to not installing any dependency that uses anything from Jon Schlinkert (so no Webpack). Unfortunately I'm stuck with gulp right now but with the next major rewrite, I'll also get rid of gulp and its 300 dependencies.

dragonwriter · 6 years ago
> Why are small packages bad?

Small, separately maintained packages are bad in a an ecosystem where a final system can incorporate only one version of any given package because it increases the number of opportunities for version conflicts.

Now, it's true that there are some other concerns which weigh in favor of packages being at the minimum useful size, but those necessarily also weigh in favor of a package management system which allows each package to isolate its upstream dependencies without constraining it's downstream users. And not just allows, but facilitates it so that it is the norm, so that dependency conflicts aren't a thing.

digianarchist · 6 years ago
This is what happens with a wafer-thin standard library.
BiteCode_dev · 6 years ago
Yes and no.

Because of the small stdlib, you need dependancies, sure.

But why this idea that each dependency must be very small, forcing you to install a hundreds childs, which, themselves, will install a hundred until the pyramid of files start to turn node_modules into a stress test?

BiteCode_dev · 6 years ago
Which is weird. I would have expected have the leftpad scandal that the community would have found a balance.

To me, culture is a feature, and despite using JS a lot since it has a monopoly on the browser, the community culture never clicked with me.

I often feel like they are reinventing the wheel, not learning from other techs solving the same problems, under and over-engineering, regularly guilty of the XKCD 927 syndrom.

Few projects taste integrated, working together, clean. The stacks looked like hacked together. Agreement on best practices and conventions are timidly showing up. It also seems popular tools are just starting to stabilize and the breaking modifications rhythm to slow down. At least more than anywhere else I worked.

There is no way I can be objective saying this, but anyone with the same feeling?

_bxg1 · 6 years ago
There are several explanations:

1) JavaScript is a first language for many, many new developers, especially the ones not coming from a formal computer science program. This is because it's multi-paradigm, extremely hireable, runs on everything, runs on the web specifically, and other reasons. If you're only going to learn one language, it's a very good and versatile choice. So its community has a lot of inexperienced members.

2) Because it's such a focal point of the industry, it gets a lot of attention from major players as well and has been evolving rapidly in terms of core features. These features usually get tested out first in the form of packages.

3) Being a dynamic language it's very easy to extend and hack. The prototype system even allows you to modify the behavior of its core primitives.

4) The more experienced devs who use it are often dissatisfied with the qualities of the base language, and so take advantage of its flexibility to try and extend it into what they think a good language is (see TypeScript, Ramda, RxJS, etc).

The response to the leftpad scandal was for NPM to disallow packages being unpublished once they've been out for 72 hours, which really should've always been the case.

dgellow · 6 years ago
I think that we are in a situation where the number of dependency doesn't have any meaning anymore for people working on web frontend projects. Recently while revamping parts of the CI/CD at $WORK, I found out that running npm install for one of our frontend project downloads around 1 million dependencies. And npm audit reports a completely ridiculous number of security issues with them.

It's just so absurd and nonsensical, nobody can understand what that even mean.

Seems to me to be a crazy huge liability, but quite a lot of people seem to be fine with that.

I don't know what to do from that information, but trying to create some awareness around the issue feels like talking about the number of operations per second a CPU does with someone who has zero idea what a realistic range is. It's seen as a high number, but doesn't have any meaning attached to it.

Edit: I don't have access to the project anymore to check more details (such as installation time), but I checked some personal notes I took when I found this out, the redacted output from npm audit is the following

    $ npm audit
    <REDACTED> vulnerabilities found - Packages audited: 927016
    Severity: <REDACTED> Low | <REDACTED> Moderate
    Done in 41.62s.

So slightly below the 1 million mark.

Edit 2: Now I'm wondering ... maybe the "packages audited" from npm audit doesn't mean "you have that number of packages, and I audited them". But if that's the case, that's a terrible UX, and I have zero idea what that number mean, which kind of support my point.

crazygringo · 6 years ago
I mean npm only has slightly more than 1 million packages, last I checked.

This doesn't make even the slightest bit of sense. Did somebody somehow just import... everything by accident? Did npm hit a bug? Is there some infinite recursion going on that eventually hits a wall?

This obviously isn't representative.

renke1 · 6 years ago
1 million packages most of which have more than a single version.
jfkebwjsbx · 6 years ago
How is this even possible?

How are there 1 million interesting packages?!

In my job we use like, I don't know, 20 libraries total, out of, say, 50 alternatives that could do the job. Including dependencies of the dependencies I don't think we reach anywhere near 100. Every dependency is discussed with the entire team when we add it.

How do you even have time to manage 1 million?!

BiteCode_dev · 6 years ago
To quote the article:

"Luckily, most of them are using the same version of lodash, which just needs one lodash to install inside node_modules. This is often not the case with real-world production projects. Sometimes, different packages require different versions of other packages."

Npm lets you install several versions of the same package. This has the advantage of saving the dev the testing of their lib with a wide range of dependencies versions: it almost always works. Unfortunately, that means most devs just choose one version and call it a day. They build libs like one would build a project: as you were alone in the world.

doubleunplussed · 6 years ago
Presumably almost all of the dependencies are transient dependencies, not direct ones.

I still can't fathom getting to a million though.

Ayesh · 6 years ago
A million dependencies sound horribly excessive, even by JS standards.
dgellow · 6 years ago
That was my reaction too.
awinter-py · 6 years ago
how long does that take to install? is there a local npm mirror / cache?
Waterluvian · 6 years ago
I have a project with a total of eight dependencies. I walked away for six months as it was stable. I come back to add a few features and npm tells me I have over 38 000 vulnerabilities of different severity levels. So many that 'npm audit' just freezes up.

So there's that too.

samsquire · 6 years ago
My stuff breaks whenever I try upgrade it. People refactoring stuff.
brobdingnagians · 6 years ago
These types of issues are the biggest reason why I avoid javascript/npm projects. I came back to a project after a few months and it was broken, had to rewrite some parts and upgrade other parts just to get it to run again.
tluyben2 · 6 years ago
I cannot understand how people work with this. I work with many different technologies and try to avoid JS, but some times I have to. The past weeks I have worked on a React Native project that was written by someone else; what a horror show. I mean it wasn't the worst code by the previous guy but even in a few months a lot is simply broken and not 'best practice' anymore. Compared to most other environments I work with, iteration is somewhat faster when you finally have everything working, but the dependencies and ecosystem is horrible imho.

A bigger issue with all of it is that in my line of work, for the backend of firmware I cannot just depend on 3rd party libs as they will get audited, so I need to audit them before. Ofcourse most people don't have that issue.

williamdclt · 6 years ago
I don't agree with you on this. The JS (or NPM) way makes it a pain to audit a project due to thousands of dependencies, but it's far easier to "have everything working".

`npm i && npm start` is as good as it gets in terms of "getting started" friction (with version locking): python is worse, ruby is worse, PHP is worse, Java is worse.

React-Native has more moving pieces indeed, but it's not really JS's (or NPM's) fault: you get all problems of iOS and Android at once. I will definitely agree with you that working on React-Native is a pain, to be able to work with modern tools need to know JS, optionally TS, Obj-C, Swift, Java, Kotlin, Ruby (for Fastlane) and Gradle's custom language. Insane.

dgellow · 6 years ago
I've done 3 years of Golang as my main language, my experience is:

- go build ./... builds everything from the current project

- go test ./... tests everything from the current project

- go install ./... installs everything from the current project

That's one of the best thing ever. I can go to any project, I know how to build, test, install, and can directly be productive.

On the other hand, I started learning C++ one year ago, and it's the exact opposite, each project has its own homemade build system that isn't supported by one platform or the other.

halostatue · 6 years ago
If only version locking weren’t fundamentally broken in JS package managers. I’ve said it a couple of other times on this, but `--frozen-lockfile` should be the default behaviour. Under no circumstances should any developer get a different package version than what is locked in the lockfile. This is the exact opposite of what npm, yarn, and pnpm do with their lockfiles (unless you use `--frozen-lockfile` or `--ci`).
stepbeek · 6 years ago
All our JVM projects use gradle. We run `gradle build` and it resolves all dependencies and runs all tests. I don't understand the claim that node has the least getting started friction.
montroser · 6 years ago
It's not really hell though, is it? Hell is infinite pain and agony. This is more like an occasional bother.

Yes, we get comically large numbers when we look through node_modules. But, real "dependency hell" is when you have situations that take unbounded manual effort to resolve.

How often do we end up with impossible circular dependencies? Or conflicting versions clobbering each other? Or non-deterministic builds introducing random behavior?

That all is commonplace even today with other platforms like python, and rarely an issue with node. I'd much rather occasionally sneer at the size of node_modules than any of that actual hell.

uhoh-itsmaciek · 6 years ago
Yes, exactly. I used to think isolating dependencies like npm/yarn do, where each package has its own copy of its own dependency tree (ignoring deduping), was crazy, and potential conflicts should be a forcing function to encourage you to minimize dependencies in the first place. But then I started using these systems and it's by far the least time I've ever spent worrying about deps. There are downsides to this and how it encourages dependency proliferation, and I still believe in minimizing your dependencies, but it's the least bad system I've seen.
ng12 · 6 years ago
In my experience is the people who complain most audibly about npm are the same folks who think nothing of spending a few days debugging classpath issues.
parhamn · 6 years ago
What’s worse is that the whole installing multiple versions of the same package for different sub dependencies doesn’t make any sense when when the language supports singletons and module level variables with has no way of specifying which version to import.

I was shocked this behavior exists when a site broke by upgrading a sub dependency. Turned out they both installed react so they used a different “creatContext” and now there were two context instances instead of one.

tristanstcyr · 6 years ago
React would normally be a peerDependency and so you shouldn't run into this problem if dependencies are specified properly.

If not, NPM module flattening should take care of it.

Then worst case, you can also tell webpack to bundle a specific installation.

There are some ways to solve these problems...

parhamn · 6 years ago
Sure (upvote) and we dealt with it accordingly after some confused digging. On a more general level I’ve never seen something like that in a package manager. It seems as though some of the language features (like the singleton I mentioned) would make it obvious that arbitrarily duplicating packages and scoping them to sub packages is a major no-no, as in most languages I’ve dealt with.
johannes1234321 · 6 years ago
My concern with npm packages isn't the number and small size of the packages itself, but that most of them are maintained and owned by individuals who do work with little review by others and who could disappear any time. If an individual goes away or takes harmful action it takes time till this is noticed and till a surviving fork emerges.

If more libraries were maintained by groups in a shared/collaborative way, more of those risks would go away.