Remix.run Logo
epage 4 days ago

Let's play this out in a compiled language like Cargo.

If every dependency was a `=` and cargo allowed multiple versions of SemVer compatible packages.

The first impact will be that your build will fail. Say you are using `regex` and you are interacting with two libraries that take a `regex::Regex`. All of the versions need to align to pass `Regex` between yourself and your dependencies.

The second impact will be that your builds will be slow. People are already annoyed when there are multiple SemVer incompatible versions of their dependencies in their dependency tree, now it can happen to any of your dependencies and you are working across your dependency tree to get everything aligned.

The third impact is if you, as the application developer, need a security fix in a transitive dependency. You now need to work through the entire bubble up process before it becomes available to you.

Ultimately, lockfiles are about giving the top-level application control over their dependency tree balanced with build times and cross-package interoperability. Similarly, SemVer is a tool any library with transitive dependencies [0]

[0] https://matklad.github.io/2024/11/23/semver-is-not-about-you...

matklad 4 days ago | parent | next [-]

This scheme _can_ be made to work in the context of Cargo. You can have all of:

* Absence of lockfiles

* Absence of the central registry

* Cryptographically checksummed dependency trees

* Semver-style unification of compatible dependencies

* Ability for the root package to override transitive dependencies

At the cost of

* minver-ish resolution semantics

* deeper critical path in terms of HTTP requests for resolving dependencies

The trick is that, rather than using crates.io as the universe of package versions to resolve against, you look only at the subset of package versions reachable from the root package. See https://matklad.github.io/2024/12/24/minimal-version-selecti...

epage 3 days ago | parent | next [-]

Note that the original article came across as there being no package unification. A later update said that versions would be selected "closest to the root".

I was speaking to that version of the article. There was no way to override transitive dependencies and no unification. When those are in the picture, the situation changes. However, that also undermines an argument of the article against SemVer

> But... why would libpupa’s author write a version range that includes versions that don’t exist yet? How could they know that liblupa 0.7.9, whenever it will be released, will continue to work with libpupa? Surely they can’t see the future? Semantic versioning is a hint, but it has never been a guarantee.

IshKebab 3 days ago | parent | prev [-]

Is that what Go does? I always thought their module versioning system sounded well thought out (though I gave up on Go before they introduced it so I have no idea how well it works in practice).

BlackFly 2 days ago | parent | prev | next [-]

> All of the versions need to align to pass `Regex` between yourself and your dependencies.

In nominally typed languages, all types have to be nominally the same, yes, but it does not follow that semver compatible packages will not permit passing different versions of each around: https://github.com/dtolnay/semver-trick (the trick is to ensure that different versions have nominally the same type by forward dependency).

Anyways, even with this, cargo has a lock file because you want to run in CI what you ran when you developed the feature instead of getting non-deterministic executions in an automated build + verification. That's what a deterministic build is aiming for. Then next feature you develop you can ignore the lockfile, pull in the new dependencies and move on with life because you don't necessarily care for determinism there: you are willing to fix issues. Or you aren't and you use the lockfile.

yawaramin 4 days ago | parent | prev | next [-]

> All of the versions need to align to pass `Regex` between yourself and your dependencies.

No, they don't. As the article explains, the resolution process will pick the version that is 'closest to the root' of the project.

> The second impact will be that your builds will be slow....you are working across your dependency tree to get everything aligned.

As mentioned earlier, no you're not. So there's nothing to support the claim that builds will be slower.

> You now need to work through the entire bubble up process before it becomes available to you.

No you don't, because as mentioned earlier, the version that is 'closest to root' will be picked. So you just specify the security fixed version as a direct dependency and you get it immediately.

hosh 4 days ago | parent | prev | next [-]

Wasn’t the article suggesting that the top level dependencies override transitive dependencies, and that could be done in the main package file instead of the lock file?

epage 3 days ago | parent [-]

That was only in an update, not in the original text.

junon 4 days ago | parent | prev | next [-]

You should not be editing your cargo.lock file manually. Cargo gives you a first-class way of overriding transitive dependencies.

richardwhiuk 4 days ago | parent [-]

You can also do cargo update -p

oblio 4 days ago | parent | prev [-]

Java is compiled, FYI.

deepsun 4 days ago | parent [-]

And interpreted.

Some call transforming .java to .class a transpilation, but then a lot of what we call compilation should also be called transpilation.

Well, Java can ALSO be AOT compiled to machine code, more popular nowadays (e.g. GraalVM).