What does HackerNews think of semver-trick?

How to avoid complicated coordinated upgrades

Language: Rust

Let's say crate B depends on crate A with a pinned dependency, and uses one of its types in a public interface.

Crate C depends on them both. It now can't bring in updates to A until B does, and when B updates that's a breaking change, so it better bump its major version.

Take a look at this trick, for example, for foundational crates updating their major version: https://github.com/dtolnay/semver-trick

Now imagine that being an issue every single patch update.

> My project will still get 1.0.2, so it will still have the security hole.

Right. To mitigate that you would regularly run `npm audit` or even just `npm upgrade` – and test afterwards of course.

I'm not completely sold, but I do think it's a very interesting idea.

> Can I ask for a warning if I'm getting two different versions linked into the same binary?

Yes. That was the lint I linked in my last post. Alternatively, you can run `cargo tree --duplicates`.

> "I really, really don't think think you should be using 1.0 any more"

That's called "yanking". Personally I think it has limited usefulness, but it exists.

https://doc.rust-lang.org/cargo/commands/cargo-yank.html

> And what happens if both versions get pulled in, but the package in question uses an external data file whose format changed between 1.0 and 1.1?

If it uses something like `include!`, both copies will be compiled in (and maybe optimized later by the linker). If it's truly "external" like hosted on some website outside the package manager, then it just means the author broke their package. Maybe I misunderstood your question.

> one of my dependencies constructs a foo using X 1.0, is that value of a different type than a foo constructed by X 1.1?

I believe they are always different types. Cargo encourages but doesn't enforce semver, so anything can change between versions, including private fields or enum variants behind non_exhaustive, etc. So they're treated as different and you need to convert between them. Although this might only be true for major versions; I don't know off the top of my head.

To work around it you can convert the types at crate boundaries, or the package author can use the so-called "semver trick" [1]

[1]: https://github.com/dtolnay/semver-trick

That's a way to avoid complicated coordinated upgrades across the ecosystem if certain conditions are met. It's called the "semver-trick" [1].

[1]: https://github.com/dtolnay/semver-trick

The JVM is 114MiB on my machine. A near-minimal ggez program in debug mode is about 100MiB,¹ and ggez is small for a Rust application library. When you start getting into the 300s of dependencies (i.e. every time I've ever got beyond a trivial desktop application), you're lucky if your release build is less than 100MiB.

Sure, I could probably halve that by forking every dependency so they aren't duplicating versions, but that's a lot of work. (It's a shame Rust doesn't let you do conditional compilation based on dependency versions, or this would be a lot easier. As it is, we have to resort to the Semver trick: https://github.com/dtolnay/semver-trick/ — not that many people do that, so it's functionally useless.)

Take GanttProject as an example. It's 20.6MiB of files, plus the JVM. I challenge any of you to make a Rust version (with accessibility support in the GUI) that can open (something resembling) its XML files and draw some (vague graphical Proof of Concept) representation on the screen (with editable text fields), in less than 114+21=135 MiB of binary. And then tell me how, because I've been trying to do that kind of thing for over a year.

¹: I can get it down to around 8MiB with release mode, lto etc., but that significantly increases the build time and only about halves the weight of the intermediate build files.

There's been a lot of attention given to this in Rust:

- Defining semver requirements for the ecosystem: https://stackoverflow.com/questions/41185023/what-exactly-is...

- There's also a library that attempts to automatically check sermver adherence of a crate: https://github.com/rust-lang/rust-semverver

- And there has been quite a bit of effort into preventing semver requirements from fracturing the ecosystem. This revolves around the compiler working with multiple major versions of a single library: https://github.com/dtolnay/semver-trick

You can sort of do this in rust too, from a programming language point of view: https://github.com/dtolnay/semver-trick