It sure is a pain that occasionally a dependency breaks. One question that one can ask, though, is whether one wants to experience that pain sooner or later. I am thinking of https://martinfowler.com/bliki/FrequencyReducesDifficulty.ht... here.
It sure is a pain that occasionally a dependency breaks. One question that one can ask, though, is whether one wants to experience that pain sooner or later. I am thinking of https://martinfowler.com/bliki/FrequencyReducesDifficulty.ht... here.
I deploy to consoles. I deploy to mobile. I deploy to the web via WASM. I deploy to windows as EXEs, MSIs, and APPXes. Your distro's linux package manager won't help me keep my dependencies up to date in any of these places. They won't even help me keep my dependencies up to date on linux, when the packages lag behind upstream - or in disconnected applications with no web access to update the linux distro with. How do I get security updates onto my OpenDingux RG350M? Through new images and newly built and pushed OPKs, not through fetched APT packages.
With versions pinned upstream at the app source code level, I can actually see what versions I've shipped had a vulnerable version, and push updates / advise my users appropriately. Stale dependencies are admittedly a concern, but they're not a concern that's even remotely fixed by having an unpinned, floating version. There's a reason software started bundling auto-updaters, and it's not because linux distros were handling everything perfectly fine already.
The article defends, among others, the Rust approach. Apps are pinned to exact versions via lockfiles. Libs are pinned to approximate verisons by semver, with CI typically catching when that breaks. This is a nice balance, and applies social pressure that encourages Rust projects to properly adhere to semver. Dependabot can flag dependencies with security issues, dependencies without security issues that are just old, auto-PR version updates to see if they pass CI or need manual action, and even auto-merge changes if you let it when the builds go green.
I've used this article as an excuse to double check my settings. I didn't have dependabot enabled for a couple of private repositories. I have a few flagged issues in an offline, user-inputless, electron clock widget I keep on my windows desktop. Harmless... but if they hadn't been? This would let me know I need to rebuild, retest, and redeploy. Via push notifcation. Despite the bundling approximating static linking. Despite the lack of linux. Despite the complete lack of an OS package manager, or even proper CI scripts. It's a much more versatile and portable approach.
For the record, I see both sides as having legitimate concerns.
Agreed.
> I suspect leaving security updates to upstream is not a recipe for secure distributions.
Agreed - but neither is leaving it to a balkanized downstream. Debian's getting picked on in another thread: https://news.ycombinator.com/item?id=26207965 .
The recipe for secure software is people doing the work, force-multiplied by cooperation and tooling. Safe languages, sane APIs, tests, CI, code reviews, audits, static analysis, dependency tooling, ease of forking when upstream is abandoned or unresponsible, easy build setups, good cross-platform support... the list goes on.
To hack on the average abandoned Rust codebase, "git clone ..." + "cargo build" alone probably just works, and I can start making, testing, and upstreaming incremental improvements one out-of-date or similarly abandoned dependency, bugfix, or feature branch at a time.
To hack on the average abandoned C codebase, I probably need new build rules and an undocumented list of extra build tools unique to this project (many of which almost certainly lack official windows binaries), and I'm probably left with a completely broken build that I can't even test my new build rules against - due to the unpinned versions causing multiple dependencies to be several major versions full of breaking changes out of sync with what the code expected. If I can be bothered to fix the build, the resulting megacommit will make any reviewer's eyes glaze over.
You can guess which of the two I'll bother trying to patch.
The only major issue was the OpenSSL fiasco that was due to overpatching upstream. Overpatching indeed should stop but is not a flaw of the package manager itself.
Disagreed.
> The only major issue was the OpenSSL fiasco that was due to overpatching upstream
There's a reason OpenSSL got forked as LibreSSL/BoringSSL - even downstream realized their approach wasn't cutting it, and they needed to go upstream and start burning everything down with fire. Granted, OpenSSL has been getting their act together - so perhaps unforking might be warranted?
Meanwhile, the CVE database continues to be flooded with memory vulnerabilities. Distros dutifully push out the patches when they get them, but that's the bare minimum.
I think that points needs to be made more often.
People focus a lot on active decisions that have the potential to introduce security vulnerabilities, but they underestimate how similar problems may exist in the status quo.
Having a single, only-one-canonical version dependency system is a recipe for package managers that update slowly because any dependency change may break an unrelated package.
Frankly, being able to say "just install every dependency and be done with it" and have Cargo/npm/etc handle all the conflicts implicitly is a huge time-saver. I think people underestimate how valuable that saved time is, both in terms of adding features and maintaining security.
That is only a problem if the old dependency no longer provides the required functionality, though. In many cases, that will never happen.
The other is that it may not always be possible/practical for other people to be at the that exact version. Some libraries can depend on system libraries that cannot be easily upgraded. E.g., numpy depends on underlying system libraries and it is not always possible to choose whatever version. GUI-libraries ultimateley depend on system GUI-libraries which also, practically speaking, cannot be updated to any random version.
Ironically, the problem you've described here originates with dependencies not having stable dependencies themselves. This is why it's so important for foundations like operating system or other platform APIs to offer excellent stability and longevity, and more generally for libraries that want to be relied upon to offer clear version information and backward-compatible updates.
One question that one can ask, though, is whether one wants to experience that pain sooner or later. I am thinking of https://martinfowler.com/bliki/FrequencyReducesDifficulty.ht... here.
But what if you never have to experience that pain at all? Or if you can experience it only once, much later, when it really does become unavoidable, instead of experiencing a similar amount of pain multiple times along the way?
Look at the sketch graph in that article, the one with time between actions and pain as the axes. Tell me what it means.
Nothing. It means nothing. It conveys literally no evidence or data of any kind. And the entire argument rests on that non-existent foundation.
They also statically link binaries, though, and an old binary could be in production for a while if the product isn’t being worked on.
The popularity of Docker files shows another way of freezing dependencies.
The general principle here is that changes get tested during the release process. You’re not creating new combinations of dependencies and running them in production without testing.
(Well, within the same binary, at least. Servers talking to each other over the network may be unexpected combinations.)
It means that as time passes by, the things that need to be done accumulate and hence the pain of doing them. You don't need evidence for that. Its a common sense.
Otherwise, give one example when waiting for action (and in absence of divine/external intervention) makes it easier. Via this logic we can deduce that there must exist 100% self-fixing entities. I am not aware of any such thing. Many entities have some autocorrection (including us) but up to a certain point and usually (only?) when life is involved.
I couldn't disagree more. It is not self-evident either that updates will inevitably need to be done or that the pain of doing them if they are needed will increase (never mind increase exponentially, as the article arbitrarily claimed) with time.
You should introduce an external dependency into your program if and only if it provides useful extra functionality and the benefits of using it outweigh the costs. You should update an existing dependency to a new version under exactly the same conditions.
Otherwise, give one example when waiting for action (and in absence of divine/external intervention) makes it easier.
When you never need to take that action at all, for example if you include a library in your program and the library does what you need and that's that.
OK, how about this: any work done has non 0 probability for mistake (now we can use math to deduce the same)
> You should introduce an external dependency into your program if and only if it provides useful extra functionality and the benefits of using it outweigh the costs. You should update an existing dependency to a new version under exactly the same conditions.
People are bad in judging such stuff hence this is tautology and provide no actionable advice.
> When you never need to take that action at all, for example if you include a library in your program and the library does what you need and that's that.
This doesn't qualify as you already said that "you dont need to take action". I said "when waiting for action" which implies action must be done at some point.