Credits: reddit/xaxaxa_trick

We all embrace the diversity of Open Source community, it sounds right, isn’t it? Everyone could contribute, I use yours, you use mine, world cannot be more perfect ever since. (I am not referring to communism by the way ;))

Until vulnerability happens, now the pain of ones will also be shared and felt by which requires it, and it becomes worse when the dependency chain grows longer and longer, you can’t even tell whether it is safe to upgrade one of those.

It happens recently as I do a npm audit for one of my projects, it shown a moderate DoS vulnerability on one of the package js-yaml prior to v3.13 .

dependency hell

There were 2 dependencies requiring it, one was eslint another was rollup-plugin-postcss , I can easily resolve eslint as I found a new version of eslint has upgrades on js-yaml . But I can’t do the same against rollup-plugin-postcss , as rollup-plugin-postcss is already the latest.

So I looked up on the dependency tree of rollup-plugin-postcss , it looked like this:

├─┬ rollup-plugin-postcss@2.0.3

│ ├─┬ postcss-load-config@2.0.0

│ │ ├─┬ cosmiconfig@4.0.0

│ │ │ ├─┬ js-yaml@3.9.0

The patches of js-yaml came up 21 days ago (by the time this blog is edited), we are seeing the direct dependent module cosmiconfig has already been using the patched js-yaml on 12 days ago, which is fabulous!

Unfortunately however, postcss-load-config seems to be a blocker, the module has not been updating 9 months till now. Even though the maintainer may not notice shortly by the latest patch – its dependency cosmiconfig hosted 12 days ago. A high probability that a patch would not be up so soon. Needless to say, the patched direct dependency is a major update from cosmiconfig@4.0.0 to cosmiconfig@5.2.0 , breaking changes could occur, indicated by semantics itself.

The dependency hell, Node.js community is famous for, having dozens of dependencies with hundreds of sub-dependencies weighted Megabytes. The patch, upgrade of a module, propagates slowly from edges to the surface, module by module, depending on the layers of dependency chain, it could take months, or years. By just looking at how old grunt/gradle’s libraries are, could you imagine how many dead dependencies are out there?

I believe this, the slow propagation of upgrades, is one of the issue Monorepo (mono repository) trying to solve, as each dependencies are in-sync, meaning that no dependency fragment would occur. But that is just an ideal paradigm, Monorepo is hard to maintain as one could break the others, DevOps builds may need to take another strategy or redesign, etc.

One approach Netflix has taken, codenamed Niagara, is they have an internal registry which pairs the new release to every versions of modules which requires it, and updating those pairs which passed the test while reporting failures to both modules’ maintainers. In this way, the dependency chain upgrades would happen seamlessly.

Another approach is having a bot constantly checking on your own required dependencies for updates, then automated testing against them and finally upgrading dependencies immediately if tests passed. However this may broke the module if the tests are not thorough.

I felt the same pain of upgrading dependency chain for my own project when I wanted to upgrade my surface application, I needed to upgrade my dependencies & dependencies of dependency as well. I guess this is an inevitable pain every modern developers have to suffer with the fast paced development world.