Hacker News new | ask | show | jobs
by pier25 2548 days ago
A priori I'm not too crazy about the architecture and nomenclature, but I have to say that having zero dependencies is a goal more projects should have.

The JS ecosystem is pretty fragile because every project depends on hundreds of other projects.

The other day I had to go back to a 2 year old Vue project. I found that many of the dependencies had vulnerabilities so I started updating those. Then nothing worked because the newer versions changed its API or didn't support such and such feature. In the end I had to reconfigure all the project from scratch. And this project was just 2 years old...

9 comments

Devil's advocate here.

If you hadn't used those libraries, you would have had to implement functionality from those libraries in your own code. Presumably, your code would be subject to the same vulnerabilities and probably require even more work to update. After all, API changes are generally easier to handle than design changes.

</advocacy>

I do agree that tiny, insignificant libraries like "left-pad" are bad. Imo, a good dependency is one that significantly reduces the design complexity of a project.

The problem is that you need one function and you're pulling an entire library. Also you have some limited use-case (e.g. you don't have to support IE 6), but libraries tend to accumulate all the cruft in the world over the years, so you're pulling and executing lots of unnecessary code. JavaScript is too dynamic to reliably delete unused code, so even approaches like tree shaking, etc do not work well. And left-pad style libraries has its own problems, yeah.

There are universal utility libraries. jQuery for DOM, underscore for general utilities. It's OK to use them, because everyone knows them. But if you miss some tiny function, just write it yourself. DRY principle often brings more harm than good.

I would argue whether DRY enforces 3rd party libraries usage. "Yourself" touches the code you wrote, noone asks you to use already written solutions.
For real, zero dependencies is as much an extreme view as infinite dependencies. There's something to be said about delegating some of the hard but ultimately not relevant problems to a library. I'm not about to roll my own datetime library or my own crypto library.
> I'm not about to roll my own datetime library or my own crypto library.

Same, but it's nice if those libraries have zero dependencies so when you include them in your project you know exactly what you're getting.

This. It's not that I want to avoid any dependency -- I'm more than happy to use a well-crafted library or tool that provides an abstraction that neatly covers a problem domain. But at some point the depth and complexity of the dependency tree can make it difficult for a package manager to keep in a correct state, which speaks to the opacity anyone who attempts to manually map it is going to end up confronting.
There are also bigger libraries like lodash that are often included for the use of a single feature.

I must admit I have the same aversion as OC. I love the ubiquity, resources and portability of JavaScript, but the rats nest of dependencies feels extremely brittle.

Not only is it often added for a single feature, that feature is more often than not already supported in vanilla JS or with ES 6.
It depends on what you're developing. If you're developing an ~app, feel free to use what you want. If you're developing a library

- have few dependencies

- favor dependencies that, themselves, have few dependencies

- favor dependencies that have a single purpose

- favor well-understood stable libraries

- favor libraries that handle breaking changes well. A good Java example is commons-lang 2 vs 3 where the package name was changed so they can coexist. A bad example is Guava.

NPM makes it possible to release new minor/patch versions even for older majors. You just need to specify in your package.json that you want that specific major version (and not any newer, which is of course common best practice) and you'll get them.
That's the culture - so many people consider it ok to break backwards compatibility, although it's almost always trivial to maintain it.
It’s hard to move fast and not break things.
If there is any time when "more haste, less speed" is appropriate, developing software is probably it.

There is no need for the library ecosystem and dependency culture around a popular programming language to be as fragile as JavaScript's. Other popular programming languages manage not to have this problem to anything like the same degree.

I doubt it’s hard it just takes discipline. Use 0. Versions while working stuff out
If the javascript community was honest, nothing would ever graduate to 1.0.
I think JS moves fast because of the sheer amount people that use it. Many have discipline, but countless others do not.
Exactly. It's more of a problem of it's community.

I don't mean offense, but JavaScript is just so accessible and easy to get into, that's it's flooded with people who haven't had time to learn the instincts that make a senior dev a "senior". The author of seems to really "get it", as do many others.

yeah there's plenty libraries out there that do a great job at providing a stable API across the years. the problem is not using libraries, but it's using unproven libraries.

to an extent that's why I don't understand the jQuery hate from modern JavaScript devs, jQuery had a stable API for years both externally and internally, so that not only it easy to upgrade it but it also easy to upgrade whatever plugin you were using.

sure it's not a framework so it doesn't really reduce how much code you write, but it's a solid foundation library

Nobody said you should move fast, except Facebook.
Easy, just few principles. Think immutability, applied to dependencies and versioning.
This is a real problem. It's one thing for apps under continual development because you can generally stay fairly current as a matter of course. Everything changes when you're working with the kind of app that hums along unchanged for years until out of nowhere, years removed from anyone even familiar with the original devs being around, some change is needed that requires unraveling the whole sweater. This is a problem for consultants, agencies, etc. who are especially ill suited to deal with this sort of valueless labor.
There's a proverb for the Go programming language which says: ,,A little copying is better than a little dependency."
> In the first version of the software, there were 70 full copies of 4 different OpenSSL versions, ranging from 0.9.8 to 1.0.2k (including one from a vendor SDK) with partial copies of 14 versions, ranging from 0.9.7d to 1.0.2k, those partial copies numbering 304. Fragments of 10 versions, ranging from 0.9.6 to 1.0.2k, were also found across the codebase, with these normally being small sets of files that had been copied to import some particular functionality.

https://www.theregister.co.uk/2019/03/28/hcsec_huawei_oversi...

One thing zero dependencies bring is the size would become huge.

Another thing is people either:

1. Use their own boring abstraction every time, and every abstraction might be different. 2. Not using abstraction at all, this makes JavaScript more like C code base.

Of course, I'm not saying it's not fragile now. But probably the answer is not as simple as dependency free. Some dialect provides interfaces (like TypeScript), type-classes (yet to be seen in mainstream dialects but I believe this really helps when dealing with fragility because Java could also be fragile because of nominal typing without having type-class) might help in the future.

On server side we might get a decent option that comes with Go like std lib Deno.
Yes, exactly! Being less-fragile is one thing. Greatly reducing possible attack vectors by almost eliminating supply chain attacks is another.
That means throwing away thousands of hours of hundreds of programming time, just to reinvent the wheel and use untested, vulnerable, and I assume buggy (from lack of testing in real world) code.
not usually, because:

1. usually a library has a wider range of features than you need. So you don't have to rewrite the whole library, just the bits you need, and you end up writing much less code because don't have to integrate the bits you need with the bits you don't need.

2. you do have to spend a lot of time understanding the interface of the imported library and writing integration code to use it correctly.

3. (as others have said) there's a lot of time spent auditing the library (and all its dependencies) to make sure it's not doing bad things. And that auditing work needs to be repeated every time any dependency of the library updates a version. This is especially important if you're doing anything with customer information involved - you're liable if you import a library that imports a dependency that quietly sends your customer's personal information to a url in Russia.

4. you may have to make architectural compromises to fit the library in; it might not support concurrent access, or idempotent calls, and that may cause more time loss in the long run than just writing what you need yourself.

5. the library becomes a black box, and your code becomes plumbing connecting the black boxes together. Trying to debug your application, or improve performance, or make it more robust, is impossible because you can't change any of the black boxes and your plumbing code does nothing important.

that's most of why I prefer to roll my own code rather than import a dependency in most cases (crypto being the notable exception).

Studying the dependencies, esp for JS, takes a lot of time as well. Before including a library, I want to know if it is maintained, if it has no known sec flaws and if the author dies I have a chance of taking it up ourselves, at least for audits and sec fixes. If you are doing npm install and further ignoring those implications, then yes, you are right, but it will bite you in the end as software outlives it’s initial intended lifespan sometimes by decades; how much VB6 is running production that was ‘just a little tool for a few months until we ...’? ... how much PHP? personally I know 1000s of those running across companies we work with. With VB6 and PHP ‘oldskool’ (meaning little or no deps in the form of downloaded libs and components) people have no issue maintaining it. But if you have to do some fixes in 2029 on a node.js API you finished yesterday, you would not be happy. With 10000s of libs that will not exist anymore, many of those having massive security issues, many API’s they call out to having changed, you would need to study and work on the entire tree that affects the changes. If you did not set this up with this in mind (which seems some kind of JS plague as you indeed also suggest), this is what will happen in many occasions.

In general; many deps is a bad idea for longevity of software. How can you know if it does not all crash and burn if the author changes jobs? Personally I want to know I have the sources and understand them enough to be able to support them with our team; in reality, for the JS ecosystem, that often means it is less work (those hours you mention) to write it yourself because it was trivial to begin with anyway and the npm solution has 1000 dependencies yours does not (for instance, they use deps like left-pad to implement something that can be implemented in 100 lines of pure JS, now needing still 100 lines but of rancid (unsupported?) one liner ‘libraries’ which is absolutely insane)

Managers usually are not programmers nor have technical background: once something works, they might not allocate time for you/your team to touch it for many years. You probably left or enjoying your pension (many anecdotes about that: a lot of companies are running on software that was done by one employee and he retired, years ago), while some poor junior who does not know what JS is, is holding your turd. At least with 20 year old PHP it still runs and without knowing PHP beforehand, it is not hard to change/fix for a capable programmer.

I have seen this with larger node.js and with Rails projects; not so much with Java and .NET projects; the latter seem to be fine many year later (maybe with some tiny migrations), even when updating libraries or runtimes.

It is a compromise ofcourse; I am not against reuse, but only to the limit we could possibly handle maintenance and updates ourselves of all imported libs. Otherwise it is a no-go.

it's not just the lib, the whole ecosystem is fucked. trying to compile a two year old gulp project is madness because all tooling have all kind of incompatibilities with their own file format and other tools invocations.

many of these only work when installed globally and conflict each other version to the point the only way to work at project of different ages are chroots or flat out vms. whomever ever thought that -g was a good idea should never work at a tooling system again.

heck, some years ago you could push a library change to npm updating a version number that already existed there, the place is a mad house.

> Studying the dependencies, esp for JS, takes a lot of time as well. Before including a library, I want to know if it is maintained, if it has no known sec flaws and if the author dies I have a chance of taking it up ourselves, at least for audits and sec fixes.

Sure, but a lot less time that planning, programming, debugging, maintaining your own library.

> In general; many deps is a bad idea for longevity of software. How can you know if it does not all crash and burn if the author changes jobs?

Many many times if a library doesn't work there are about 100 other ones that do the same thing.

> At least with 20 year old PHP it still runs and without knowing PHP beforehand, it is not hard to change/fix for a capable programmer.

How does it run? It's not even maintained anymore nor installable in any modern server. You can run it, and be exposed to all kinds of security holes.

> It is a compromise ofcourse; I am not against reuse, but only to the limit we could possibly handle maintenance and updates ourselves of all imported libs. Otherwise it is a no-go.

There's a much higher change of a single developer falling out of love/not having time for a project anymore than a major library becoming unmaintained or having a serious bug unfixed.

If that happens, you just swap it with another one. It takes 1/100th of the time compared to do everything yourself.