Background
I manage a medium sized platform for developers creating web games. This is a non-profit community project with about 20 games only supported by donations and voluntary help from contributors. We don't have customers or deadlines. This means that a little bit of instability is not a deal breaker for us: we can spend some time fixing breaking changes introduced by dependencies to get a better project afterwards.
Why I want to update all version requirements
Could you speak to why you are wanting to update all of your version requirements?
The two main reasons is that I believe newer versions are better and that regular updates are easier to manage. Letting dependents use older versions of transitive dependencies is a source of issues.
Newer is better
New versions often have new features, fixes, performance, documentation improvements. I want my projects and dependent projects to benefit from these improvements as soon as possible. This also aligns with cargo's default range for resolution (^
, highest version with matching first non-zero value). I use the default range for my projects.
What happens is the following:
- My dependents install my lib, and most of them will get the latest compatible version of the transitive dependencies.
- So I should test my lib with the latest compatible version of my dependencies.
- So all my dependents should use the latest compatible version: it's the one that was tested.
If my lib foo:1.0.0
depends on bar:^1.2.3
and they release bar:1.2.4
then I should test foo
with this version and update my requirements to bar:^1.2.4
.
What can go wrong with a mismatch between the tested version and older requirement
I can't just leave ^1.2.3
when I release foo:1.0.1
. My dependent projects will already have a Cargo.lock
with bar:1.2.3
. Updating foo
from 1.0.0
to 1.0.1
would keep bar:1.2.3
if I don't change the requirements. This is because cargo is conservative when a Cargo.lock
already exists. I need to update my requirements to force an update of the transitive dependencies.
This is not some theoretical concern: I had this exact issue with scrypt
because it was tested with a more recent patch version but did not update its requirements properly.
Large windows of compatible versions are riskier
The more general feeling I have is that semver ranges are good as they allow to get the benefits mentioned at the start of this section for all transitive dependencies, however maintainers should also strive to keep the highest requirements possible so the number of possible versions remains fairly low.
This allows to reduce variations and accidental compatibility problems. From this point of view, cargo upgrade
is the complement of cargo publish
: they both move the window of compatible versions, just different ends.
In particular, I want the latest version even if it is a semver breaking change. I am working on my project at the moment: let's update to the latest version and push it to dependents, as it will be the only supported version going forward anyway.
So count me in the opposition to low requirements 
There seemed to be general interest in keeping library version requirements low within a major version but to upgrade across major versions.
Regular updates are easier
I described why I want to use the latest version and pass them down in my requirements. Being able to track the latest version becomes important then. I occasionally go through all my Cargo.toml
files and check crates.io
if there are new versions. However doing this manually is a slow process.
An important use case for me is then to use cargo upgrade
as some sort of local "dependabot". I can run it in my project and it will update all my manifests. (Small aside: I wish cargo upgrade
had better support workspaces, right now it bails out as soon as it hits a dependency not on crates.io
even if it exists in the workspace).
This usage of cargo upgrade
lets me check my Git diff, try to compile, run my tests. In the majority of cases, this allows me to confidently commit the updated dependencies.
For situations when there are breaking changes, this usually does not involve a lot of work due to to Rust's help, changelogs (or simply checking the commits). However, it becomes harder if I don´t update regularly as then more changes will have piled up. Having a fast way to upgrade all dependencies is important to me to avoid maintenance spikes.
By the way, I much prefer a command to update all the manifests and then let me edit the changes in my editor instead of an interactive command. It's faster for me to use my Git diffs and editor if I need to undo a couple upgrades. This is my main issue with yarn up
: it's cumbersome to update everything, the default for this is to use the interactive mode (you may add yarn up to the prior art).
Opinion on the proposal / points of concern
First of all, sorry for my previous message which was mostly originating from the GitHub issue on the latest cargo-edit
changes and was a bit out of topic as it did not reply to the proposal at the top of this thread. I am a regular visitor of this forum, but post fairly rarely, As I was asked, I'll share my opinion on the proposal
Could you speak to how the proposed behavior under Known Points of Concern 1 would work for you or not? Having your input on expected future behavior would be useful so both of us are not surprised on the next iteration of behavior.
First of all I really like the overall feature and proposed notes: keep the existing precision or skipping explicitly pinned versions.
My main disagreement is with the following point:
- By default, it deals with breaking changes only. To instead upgrade minimum versions, pass
--to-lockfile
.
As I explained above I believe that we should strive to have the highest requirements possible to limit variance and ensure dependents actually get transitive dependency updates. I feel that updating all compatible requirements by default, and having a flag for incompatible requirements may be a better default.
I also find the flag --to-lockfile
confusing, in my mind it should be --from-lockfile
as we apply versions from the lockfile into the manifests. I guess the intended meaning is "to [version in the] lockfile", but it's not that clear to me.
Regarding the concern 1 ("Distinction between cargo update
and cargo upgrade
may be confusing"), I really like the approach to merge update
and upgrade
and the proposal currently in the original post (initially posted there). In particular, I like the formulation " upgrade
interacts with workspace members (manifests) while update
interacts with the workspace itself (lock file)". Having --save
as a flag to pick the mode works well for me.
Anything bringing the manifest and lockfile closer to each other is a good thing for me. So having a single command to manage both kinds of files is pretty clear.
My use case should be well covered by the proposed cargo update --incompatible --save
flags.
I like this idea a lot, --save
is a bit ambiguous; --manifest
or similar is clearer to indicate the new mode.