-
Notifications
You must be signed in to change notification settings - Fork 147
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Questions about versioning Gloo #66
Comments
Now hopefully we won't run into the issue of breaking changes very often at all, since our API design process attempts to front-load a lot of questions that if left unanswered would become breaking changes down the line. But it is inevitable that we will eventually run into some scenario where we must decide whether to do a breaking change or not. My personal opinion is that we should make an effort to minimize breaking changes, but that we shouldn't bend over backwards to do so. We should definitely coalesce many small breaking bumps into one release when possible. However, due to the way that breaking bumps cascade up to the umbrella crate, it seems like it will be quite hard to completely avoid breaking changes at that level. We should accept that as a reality. Regarding versioning the umbrella crate and the individual crates, I think we should attempt to version them independently. This would alleviate some of the pain of breaking version bumps, since individual crates will have a less tumultuous version history than the umbrella crate which must do a breaking change if any of the other crates do. A little extra maintenance outweighs a bunch of user-facing pain here. |
My opinion is: avoid breaking changes when possible. And when breaking changes become necessary, try to put multiple breaking changes into a single release. And only do breaking changes once in a while (let's say, one breaking release every 6 months, not sooner). This allows us to (slowly) fix bad APIs by doing breaking changes, but minimizes the amount of churn for consumers (since they only need to upgrade once every 6 months). And obviously it should follow the proper deprecation cycle, to give time for users to upgrade.
I say no. Each should be versioned independently.
Is it a big problem? Can we fix it? For example, we can do lockstep releases where we publish all changed packages at the same time (even though each package might be at a different version). |
I think we shouldn't change the version of crates that weren't modified:
However, if the version of The problem that I see is this: When One way to remedy this, is to release new APIs as standalone crates, and include them in |
Why would it skip |
So it has the same version as the umbrella crate. It's just an idea. |
Okay... but why? What benefit does that have? Why only sync the versions sometimes and not other times? |
It means that, by looking at the version numbers, you immediately see which crates were updated in the last release (because their versions match the version of |
Ah, okay, so you're saying that the That's a valid idea, but personally, I don't think it's necessary: the |
breaking changesDefinitely agree with the sentiment you are presenting overall @fitzgen. We should minimize breaking changes as much as possible. The front-loaded design process will definitely help with that. That being said, we shouldn't completely eschew breaking changes. Grouping them together is a good call as well. child / parent version syncKeeping the child crates' versions pinned to the parent create seems like a pretty serious maintenance overhead, and I doesn't seem like we would gain much from doing this. So, I agree with @fitzgen's initial statements on versioning child crates independently. deprecation / breaking release cycleFull agreement with @Pauan's analysis above. versioning parent crateTechnically, if the parent Consider the following example.
If some updates are made to What do we do to the parent
Next, a similar update hits
|
I don't think it is necessary, according to the rules of semver. In fact, the So it is only when a breaking change occurs that we need to even touch the |
That would only be true if we are not attempting to present the parent crate as an API in and of itself.
I agree that we could choose to only version the parent on breaking changes from a child, but what we loose is the ability to refer to the overall state of the Gloo API. Take the tokio project as an example. They've been doing the same thing I have suggested above (successfully, it would seem). For a supporting example of this versioning behavior, check out this recent tokio release. You can see from the tokio changelog (which is analogous to our parent Gloo crate) that it was bumped from
To summarize: if we are not actually going to use the parent crate as a representation of the Gloo API overall, then there really is not much need for it in the first place. If it is representative of our Gloo API overall, then according to semver, we should version it accordingly. Seems logical to follow tokio's example on the versioning front, IMHO. |
Yes, the primary purpose is for convenience. This is emphasized even further by the fact that As a general rule, libraries should depend upon individual crates, and applications should use the umbrella crate (purely for convenience).
I do not think so. Convenience is quite important. If it weren't, then there would be no need for
Taking a look at their release system, it seems the primary reason they bump the main version is because they also bump the In most cases they don't even bump the There are multiple ways to handle these "umbrella" crates ( So that means we have essentially three different systems:
I don't have strong opinions about any of these systems, they each have their pros and cons. I think option 1 is the worst, since it's high maintenance, and it forces version bumps even when not needed. However, it does make it clear that the sub-crates are related to the main crate. I think option 2 is reasonable, but it has some maintenance burden, and it means that the main crate will have a version which is much higher than the version of any of its sub-crates. I think option 3 is the least maintenance, though users might incorrectly believe that the Gloo API isn't changing (even though it is). |
Initially my gut instinct was very much against letting cargo/semver do nested updates without bumping the umbrella crate version, but I find myself unable to construct a strong argument from first principles, so I guess it seems OK to me. The one thing I will note is that we haven't ruled out the
Yeah, these are the main options I see. I think option (1) is easiest from a maintenance perspective, since it is very easy to script publishing and updates (as we've done with I was in favor of option (2) previously, but as stated above the horizontal rule, I guess I am now in favor of (3). (Although I'd note we would also need to bump the minor version when adding new |
Right, I didn't mean to imply that we'd literally never do minor bumps, just that we wouldn't bump it solely for child crates updating. |
One thing to consider: I haven't tested it, but I believe crates.io only regenerates docs when the version is bumped, so that's an argument in favor of 2, since we want new APIs to show up in the |
@fitzgen && @Pauan I definitely think that option 3 is easiest for us as maintainers, but not for users. I would like to critically emphasize that option 3 is not semver compliant. Let us remember that semver applies to whatever is declared to be the "public API" of some software system. Just consider the following scenario: A user depends on the parent gloo crate, and they are using a few of the subcrates. Let's say they are depending on
I hope I am clearly expressing the semver violation. I hope I am not communicating abrasively or anything like that, but I guarantee that this will cause problems for users. We have probably all experienced these sorts of issues before. I know I certainly have. The typical counter argument to this is: "well, just have the users pin the exact version of the child crates which they depend on."
In the example of tokio which I mentioned above (which is accurate), the parent |
I would like to offer an additional point of clarification here, in case there is confusion. If the parent Gloo crate had its own code, and was not re-exporting child crates as part of its API, then option 3 would not only be the best way to go, it would be the only pattern which would be semver compliant. However, because we are publicly re-exporting the code of child crates in the parent Gloo crate, according to the Rust language semantics, these are now part of the crate's public API. As such, changes to that re-exported code now constitute as API changes, by definition. |
This is true of bugs introduced in all semver-compatible releases.
I don't think anyone is arguing that isn't the case. You could consider that a bug fixed in But a new bug introduced in a new, semver-compatible The only way to avoid new bugs is to pin exact version dependencies:
Now we have two copies of |
Yes, but the differences is that in a semver-compatible release, you have a new version to point to, like gloo |
As far as the last question, no. I do not think we will need to do that. We can still express the subcrate dependencies in standard "compatibility" format (eg, The Cargo.toml will need to be updated in the parent as well, because if we release a parent version of |
@fitzgen what you are saying is true that if we release versions of child crates which are backwards compatible, they will be pulled in as part of a I think that it would be best not to pin them, because if it is just a single child crate which has the bug in it, then a user can easily patch that in their Cargo.toml until we get a new release out, without the user having to potentially revert other features which they need. Avoiding duplicates is good, for sure. That is usually a pain as well. I think that following the pattern which tokio uses is probably a good bet for us as well. Update parent crate to reflect real API changes, but do not strictly pin child deps, as this allows users to patch versions as needed. |
That's true with all the options though, because umbrella crates do not pin at specific versions, so cargo will always pull in the latest version of sub-crates. So downgrading to a previous version of The only way to solve that problem is to downgrade to a previous
This is true with all the options though.
Pinning does not help with any of the options. Only
I don't think you're being abrasive, and I don't see how presenting neutral arguments could be seen as abrasive. And I don't want to encourage a culture that views neutral arguments as being abrasive, since that's very harmful toward progress.
Absolutely, which is exactly what
Except it doesn't, because sub-crates will still be updated regardless. And downgrading to a previous version of |
I thought I might jump in as well and give my opinion on this. I've tried to read this thread and get up to speed, but sorry if I duplicate something! I agree that the options @Pauan outlines above are a good representation of the state of play. My personal opinion is that option (2) is the best. In my own words I would expect that whenever a crate is bumped all crates which depend on it have their version requirement on that crate bumped. Those crates themselves get a bump, and it continues up to the root crate bumping crates. (sort of a cascading bump, but crates which don't depend on the original crate at all aren't bumped). My rationale for this is slightly different from what's already mentioned though. FWIW @thedodd I believe @Pauan's most recent comment is correct. Unless you're using pinned dependency versions ( Rather my thinking of choosing this cascading bump is to have something to talk about. I think the I do agree that this strategy can be more burdensome on maintainers, but this is I think imminently solvable with a publication script like |
@alexcrichton yea, when dealing with a parent crate which re-exports child crates without exact version pinning, that is definitely the case. I suppose the main thing I am attempting to communicate is the importance of public API representation in the parent Gloo crate. Though cargo uses "latest compatible" semantics by default, I do still think there is quite a lot of value in being able to refer to public API versions, even if it is only in terms of documentation and features. @Pauan, what you are saying about the lock file is certainly correct as well. |
@thedodd I think I agree yeah about being able to talk about |
That's a really good point that I hadn't thought of. If nothing else, it serves as marketing, showing other people that the crate is alive and receiving steady updates. Given the above (and the obvious benefits of regenerating the docs), I'm now also in favor of option 2. |
So it seems to me like we have consensus on option (2). I'll write up a couple sentence summary of this approach for the Does anyone have concerns with moving forward in this way? |
Pull request over in #74 |
Just coming across this issue — I agree that option (2) seems the best, and have no objections. The PR also looks good to me. |
There's a lot of great information in this thread and I've seen it linked to elsewhere for discussions of managing workspaces so I'd like to just clarify a couple of small things:
This is almost certainly just an artifact of the extended alpha that
Cargo doesn't allow multiple semver compatible versions of a crate so this would result in a resolution failure (this might change with public/private dependencies). Something I didn't see mentioned is supporting |
I'd like to ask two questions related to versioning:
gloo
crate's version and eachgloo_whatever
crate's version?Note: I'm assuming that whatever the answers, we are following cargo's interpretation of semver.
Backwards Compatibility
On one end of the spectrum is never breaking backwards compatibility. This has the advantage of making upgrades to the latest version of crates easy for users. The disadvantage is that our APIs, once published, are set in stone: we can only add new APIs, can't ever fix flaws in existing APIs.
The Rust
std
library takes this to an extreme, where backwards compatibility is guaranteed indefinitely.wasm-bindgen
is a little looser in that we haven't ruled out breaking version bumps, but we are trying pretty hard to avoid them. We will likely have a single breaking version before moving to 1.0, after which we likely won't do breaking changes anymore.The other end of the spectrum is fixing flawed APIs whenever we notice them and releasing breaking version bumps immediately. The advantages are that we can fix flaws immediately, and don't have to maintain code that we don't believe meets our design standards. The disadvantages are more difficult upgrades for users, so they are less likely to get the latest and greatest bug fixes and perf improvements.
Versioning the Umbrella Crate and the Individual Crates
Do we want to keep each individual
gloo_whatever
crate's version locked to the same version as the umbrellagloo
crate or not?Imagine this scenario: we have
gloo
,gloo_timers
, andgloo_events
all at version0.1.2
:Now we fix a bug in
gloo_timers
that causes us to bump its version from0.1.2
to0.1.3
. To get the fix in the umbrellagloo
crate, we have to bump it to0.1.3
as well. But do we also bumpgloo_events
to0.1.3
or do we leave it at0.1.2
?The advantages of keeping everything in lock step is largely a lighter maintenance burden for ourselves, the authors/maintainers of Gloo. The disadvantage is unnecessary version bumps for toolkit crates. This disadvantage is magnified by breaking version bumps: in the previous example, if it was a breaking bump to
0.2.0
instead of a compatible bump to0.1.3
, then we could force an unnecessary breaking bump ongloo_events
, which would be kind of crappy for anyone using that crate by itself without the rest of Gloo.The advantages and disadvantages of doing the minimum cascading version bumps are basically the inverse of those for keeping everything in lock step. Upgrades are easier for users (particularly those that are using individual crates, and not the umbrella crate). However, the maintenance burden is larger for Gloo maintainers.
Note that a breaking change in a
gloo_whatever
crate will always cascade as a breaking change in the umbrellagloo
crate.The text was updated successfully, but these errors were encountered: