That's an entirely fair point and, as I got into a bit in the versioning[0] section, something that I'm giving a good deal of thought too.
I'm currently leaning towards tracking the Rakudo[1] compiler releases (~monthly), so updates wouldn't be anything like daily. As far *breaking* changes go – well, again, I'm still thinking about/discussing what guarantees to make, but I'm hoping to be able to promise to (try to) provide strong backwards compatibility. One thing I mentioned in the post is that Raku's strong support for multiple dispatch[2] makes backwards compatibility a bit easier: `_` can add a new version of a function without impacting the existing one.
That still leaves _accidental_ breakage (i.e., bugs) – which is the area I'm currently most concerned about. If not handled correctly, a utility package risks creating its own sort of internal dependency hell: if there's a bug in one sub-package that you use, it could potentially block you from using that version – even if a different sub-package has a feature you want. I'm not sure of the best solution yet, but I'm exploring a few Raku options that I think may let me provide versions at the sub-package level (or maybe even the function level?). That's very much a WIP for now, but it's something that'll happen before a 1.0.0 release.
[0]: https://raku-advent.blog/2021/12/11/unix_philosophy_without_...
[1]: https://rakudo.org/
I get the malware concerns but in practice I don't think they are such a big blocker.
They are in many languages. Of those I'm familiar with, Raku, Rust, and JavaScript all have immutable package repos. (npm wasn't when left-pad was pulled but has changed since then).
Of course, in each case they're only "immutable" in the sense that some organization (with varying degrees of centralization) has promised to host them forever; people clearly vary in their willingness to believe promises of that nature.
Still won't help you if the leftpad dev wanted to send a message/protest could have put a small update that would do something bad.
The problem is when you are not the idiot that installs leftpad but you need to install some other package like some GUI or testing framework and those "smart" devs decided to depend on leftpad directly or indirectly because some stupid philosophy. I have inherited a project with such kind of idiotic dependencies , inlcuding small stupid shit or packages with incorrect package.json that depend on things they do not actually depend or things they should not .
But then you still have issues with packages depend on npm website existing in future or even some packages are just linking to a git repo directly so if the repo is gone or giuthub is gone you(or others) can't re-create your project.
This depends heavily on the language/ecosystem. For example, golang's Minimal Version Selection[0] basically requires libraries to specify an exact version – the only way they'd get a higher one is if another library in the dependency graph had manually upgraded to the higher version.
But yeah, if the source is hosted externally and you don't have a local copy somewhere, then that's going to hurt. Which is (part of) why "should I vendor my dependencies" is such a perennial topic.
Is not only this, like what if I create an open source thing and share it on github/npm or whatever packages website, the best practice is not to bundle my dependencies and just list them. Then 5 years later someone wants to install my package that depends on their package that depends on some leftpad isOdd package that now is gone. In other ecosystems it is acceptable as a good practice that beside sthe sources you offer an .exe,.dll, .jar ,.tar.gz but in node and python community I see that the developers only distrbute now with npm, pip or similar .
Part of the solution would be to put important core stuff in the standard library , then somehow we need to stop the CV driven development that causes this fragmentation and many alternatives for same thing that you don't get a clear answer that should you use for X.
Security updates are important, but it's not like CVEs are particularly common in 5 functions like left-pad, and bloated code that isn't reachable in your app is probably not going to be an attack surface.
Especially if a dead code remover gets it.
There is a dial from small to large (in terms of code size or feature set), static or growing feature set, pinned to floating dependencies (if you are notified on available updates to your pinned version and review them, which is actually possible for small dependencies, it's largely equivalent to floating).
I don't think anyone is going to be able to convincingly say "my choice of large, growing, floating is best", or even though it's my starting preference, convincingly say "small, static, pinned is best". If you don't have the features or performance you need yet, you can make a good choice in picking a growing, floating dependency -- your call.
But there is absolutely in my mind a need for greater general discipline in dependency capabilities. We don't need a monad stack or effects system in order to say "you can't add code to remote download, deserialize, and execute, in a call to log, that's just not a sensible thing to do". Or maybe we do, because much of us still haven't learned this lesson.
Facebook in mobile applications is a perfect example. Not a security issue, but the two crash incidents last year caused some havoc for iOS developers. But as far as I am aware the only way to get Facebook login in your mobile app is to use the SDK, and no product manager on the planet is going to let engineering talk them out of Facebook user support.
Log4j type libraries don't really fall into that camp (unless it's an awful transitively forced dependency, which unfortunately can happen, but it's at least usually slightly easier to fight against/mitigate). And I was mainly challenging an implied equivalence between "large, growing, pinned dependencies plus dead code elimination", and "small, static, pinned/floating dependencies". There are plausible trade-offs that could cause you to choose any combination of those factors, but I think it's wrong if one were to make an equivalence like that.
And of course that npm allowed unilaterally pulling packages and breaking all dependents.