A brief but important point is that this primarily holds true in the context of rewriting/vendoring utilities yourself, not when discussing importing small vs. large dependencies.
Just because dependencies do a lot more than you need, doesn't mean you should automatically reach for the smallest dependency that fits your needs.
If you need 5 of the dozens of Lodash functions, for instance, it might be best to just install Lodash and let your build step shake out any unused code, rather than importing 5 new dependencies, each with far fewer eyes and release-management best practices than the Lodash maintainers have.
The argument wasn’t to import five dependencies, one for each of the functions, but to write the five functions yourself. Heck, you don’t even need to literally write them, check the Lodash source and copy them to your code.
This might be fine for some utility functions which you can tell at a glance have no errors, but for anything complex, if you copy you don't get any of the bug/security fixes that upstream will provide automatically. Oh, now you need a shim of this call to work on the latest Chrome because they killed an api- you're on your own or you have to read all of the release notes for a dependency you don't even have! But taking a dependency on some other library is, as you note, always fraught. Especially because of transitive dependencies, you end up having quite a target surface area for every dep you take.
Whether to take a dependency is a tricky thing that really comes down to engineering judgement- the thing that you (the developer) are paid to make the calls on.
The massive amount of transitive dependencies is exactly the problem with regard to auditing them. There are successful businesses built solely around auditing project dependencies and alerting teams of security issues, and they make money at all because of the labor required to maintain this machine.
It’s not even a judgement call at this point. It’s more aligned with buckling your seatbelt, pointing your car off the road, closing your eyes, flooring it and hoping for a happy ending.
If it works, why do so? Unless there's a clear performance boost, and if so you already know the code and can quickly locate your interpreted version.
Or At the time of adding you can add a NOTE or FIXME comment stating where you copied it from. A quick grep for such keyword can give you a nice overview of nice to have stuff. You can also add a ticket with all the details if you're using a project management tool and resuscitate it when that hypothetical moment happens.
The point here isn’t a specific library. It’s not even one specific language or runtime. No one is talking about literally five functions. Let’s not be pedantic and lose sight of the major point.
I get that, but if you’ve ever tried to extract a single utility function from lodash, you know that it may not be as simple as copy-pasting a single function.
If you are going to be that specific, then it would be good to post an example. If I remember correctly, lodash has some functions, that would be table stakes in functional languages, or easily built in functional languages. If such a function is difficult to extract, then it might be a good candidate to write in JS itself, which does have some of the typical tools, like map, reduce, and things like compose are easy to write oneself and part of every FP beginner tutorial. If such a function is difficult to extract, then perhaps lodash's design is not all that great. Maybe one could also copy them from elsewhere, where the code is more modular.
But again, if the discussion is going to be that specific, then you would need to provide actual examples, so that we could judge, whether we would implement that ourselves or it would be difficult to do so. Note, that often it is also not required for ones use-case, to have a 100% matching behavior either. The goal is not to duplicate lodash. The purpose of the extracted or reimplemented function would still be ones own project, where the job of that function might be much more limited.
OK in this case it looks like it is doing a lot of at runtime checking of arguments to treat them differently, based on what type of argument they are. If we restrict use to only work with arrays, or whatever we have in our project, where we need `difference`, then it should become much simpler and an easy rewrite. An alternative could be to have another argument, that is the function that gives us the `next` thing. Then the logic for that is to be specified by the caller.
Tree shaking however, will not help you, if you have to first install a library using NPM. It will only help you reduce overhead in the code served to a browser. Malicious code can run much earlier, and would be avoided, if you rewrite or extract relevant code from a library, avoiding to install the library using NPM. Or is there some pre-installation tree shaking, that I am unaware of? That would actually be interesting.
Yeah, but perhaps we could have different flavors. If you like functional style you could have a very functional standard library that doesn't mutate anything, or if you like object oriented stuff you could have classes of object with methods that mutate themselves. And the Typescript folks could have a strongly typed library.
I think the level of protection you get from that depends on how the unused code detection interacts with whatever tricks someone is using for malicious code.
Just because dependencies do a lot more than you need, doesn't mean you should automatically reach for the smallest dependency that fits your needs.
If you need 5 of the dozens of Lodash functions, for instance, it might be best to just install Lodash and let your build step shake out any unused code, rather than importing 5 new dependencies, each with far fewer eyes and release-management best practices than the Lodash maintainers have.