Releasing as 1.0.0 (instead of 0.3.0) would put the rust async ecosystem and futures maintainers in a better position to make changes going forward. Specifically it allows grouping subsequent changes into MINOR compatibility concerns (e.g. 1.1.0 for MSRV and other dependency upgrades and new features) as distinguished from PATCH-safe concerns (e.g. 1.0.1 for bug fixes, low-risk performance improvements, etc.). It also provides more versioning space for backporting fixes or other maintenance to older MAJOR or MINOR releases, as needed. This makes it easier to consume these updates downstream and should lead to a more stable ecosystem.
_Future MAJOR breaking changes would require versions 2.0.0, 3.0.0, etc._ That should not be any more problematic to maintainers or downstream users than versions 0.4.0, 0.5.0, etc. would be.
_Downstream dependencies will need editing._ I would presume the next non-alpha release will again be called _futures_, not _futures-preview_, so all downstream projects will be editing their futures Cargo.toml dependencies to move off 0.2 or some futures-preview 0.3.0-alpha.
_The Rust ecosystem at large has frequently been reluctant to make 1.x releases._ Some 0.x crate maintainers feel their crate is insufficiently mature, e.g. having planned one more additional feature for 1.x. Others, conversely and ironically, have not had a major enough change to _warrant_ the MAJOR version increase from 0.x to 1.0.0.
Meanwhile, many heavily used, foundational crates _have_ made 1.x or subsequent major releases (tempfile 3, proc-macro2 1, lazy_static 1, etc.), and there is no evidence of those maintainers regretting the 1.x release. Thus the 1.x release appears to be more of a perceptual hurdle than a practical concern.
https://semver.org/#how-do-i-know-when-to-release-100
How do I know when to release 1.0.0?
If your software is being used in production, it should probably already be 1.0.0. If you have a stable API on which users have come to depend, you should be 1.0.0. If you’re worrying a lot about backwards compatibility, you should probably already be 1.0.0.
Rust ecosystem and tooling departures from Semantic Versioning are a more complex subject, but narrowly, is this next release of _futures_ not intended for production? There is plenty of evidence of futures 0.1 being in production already.
Just as a meta note, there's been a bunch of breaking changes recently (just yesterday, in fact!), so I wouldn't exactly call Futures 0.3 stable. This is in contrast to Futures 0.1 which is stable.
The pain of the breaking changes is small right now because few people use Futures 0.3, but once it gets promoted to 1.0 it will be much harder to make breaking changes.
once it gets promoted to 1.0 it will be much harder to make breaking changes.
How so? As compared with releasing a final (non-alpha) 0.3.0, 1.x only adds the ability to release MINOR updates (e.g. 1.1.0) for things like new features or other changes considered a _minor_ compatibly risk, reserving PATCH updates for safer changes. For some later, broadly breaking changes to public APIs, releasing futures 2.x is no more technically difficult or disruptive than releasing futures 0.4.x would be.
@dekellum According to the semver spec, any 0.x release is inherently unstable, and thus it can make breaking changes at any time (without a version bump). In other words, "buyer beware".
The point of the 0.x releases is for unstable packages which are still experimenting and have not yet found the right API. That describes futures 0.3.x perfectly.
Many dozens of breaking changes have already been made on the 0.3.x version (including a couple days ago), and that's perfectly fine according to the semver spec. That's why we're still on 0.3.x and not 0.25.x.
But 1.x (and onwards) are different. They have strict requirements for stability. If you make any breaking change, no matter how tiny, it must go into a new major release. And that means that everybody depending on futures needs to manually upgrade to the new version.
If they don't, you get version conflicts, because a type defined in 1.x will be different than the same type defined in 2.x, because Rust uses nominal typing. This is very different from structurally typed languages like JavaScript or Python which don't have that problem. Nominally typed languages like Rust need to be much more careful with how they handle breaking changes.
Unlike structural languages, breaking changes in nominal languages (like Rust) cause severe fragmentation and makes it very difficult (or impossible) to use those crates as dependencies. This causes a huge amount of pain, as we have seen in the past when other crates made breaking changes. I think you are underestimating how difficult it is to migrate a popular crate to a new major version.
Because migrating to a new version can take months, it wouldn't make sense to release a new major version every week. But that's what is basically happening right now (we still have frequent breaking changes in futures 0.3 on a regular basis).
The Rust community is not oblivious to semver, instead it's quite the opposite: Rust takes semver very seriously (more seriously than other languages in my opinion), and we follow it strictly (we even have official guidelines on how to follow semver correctly). That's precisely why we avoid releasing a 1.x version until after the API has stabilized.
Releasing a 1.x release would give a false sense of stability, because everybody would think, "aha, now it is stable, so I can start building production things with it!" but then 2 weeks later we release 2.x, and then another 2 weeks later we release 3.x... so it wasn't as stable or production ready as people thought.
Of course we all want a 1.x release, but it will be released when it's ready, not during a time when breaking changes are still happening frequently.
Just as a meta note, there's been a bunch of breaking changes recently (just yesterday, in fact!), so I wouldn't exactly call Futures 0.3 stable. This is in contrast to Futures 0.1 which _is_ stable.
Many dozens of breaking changes have already been made on the
0.3.xversion (including a couple days ago), and that's perfectly fine according to the semver spec. That's why we're still on0.3.xand not0.25.x.
It is the pre-release version (0.3.0-x), not the patch version (0.3.x), that semver allows for breaking changes.
Once 0.3.0 (not pre-release) is released, we will not make breaking changes until the next minor or major version. (The exception is when there is a security problem.)
@taiki-e As semver.org says:
Major version zero (0.y.z) is for initial development. Anything MAY change at any time. The public API SHOULD NOT be considered stable.
Pre-release is a separate concept, you can have pre-releases even for 1.x, 2.x, etc.
A pre-release version MAY be denoted by appending a hyphen and a series of dot separated identifiers immediately following the patch version. Identifiers MUST comprise only ASCII alphanumerics and hyphen [0-9A-Za-z-]. Identifiers MUST NOT be empty. Numeric identifiers MUST NOT include leading zeroes. Pre-release versions have a lower precedence than the associated normal version. A pre-release version indicates that the version is unstable and might not satisfy the intended compatibility requirements as denoted by its associated normal version. Examples: 1.0.0-alpha, 1.0.0-alpha.1, 1.0.0-0.3.7, 1.0.0-x.7.z.92.
Pre-release is intended as basically a "beta" of the next release, so people can try it out before the official release happens, and then later the pre-release is promoted to a proper release. It's not the same as 0.x (which indicates perma-unstable).
It just so happens that futures uses both 0.3 and the -alpha pre-release tag, so that means it's a pre-release of the unstable 0.3 version.
futures can choose to use whatever release strategy it wants, but the official semver spec is clear in this situation.
The semver spec documents a minimum set of requirements that a package must satisfy, you are allowed to guarantee more than it says. Because of how Cargo’s pre-1.0 default caret bounds work it is pretty much expected that Rust crates will treat 0.x.* as the equivalent of x.*.0 versions for semver compatibility. It would not be _wrong_ to release breaking changes in them by just the semver spec, but it would be wrong according to community norms promoted by the official tooling.
The below agrees, and as a practical matter, has tended to allow some foundational crates to linger below 1.x for longer than SemVer (see first quote) intends.
https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html
While SemVer says there is no compatibility before 1.0.0, Cargo considers 0.x.y to be compatible with 0.x.z, where y ≥ z and x > 0.
Releases ≥ 1.0.0 are both less confusing (per above, Rust norms/tools and SemVer being more in agreement) and more flexible/expressive, since both MINOR (1.1.0) and PATCH (1.0.1) releases are considered backward _compatible_.
@dekellum I agree, production code should not rely on 0.x crates, but unfortunately Cargo makes it easy to do so (because it creates extra stability that normally wouldn't exist).
However I still don't agree with releasing 1.x at this time, because despite the behavior of Cargo, there is still a community attitude that 1.x means "stable and production ready" and 0.x means "unstable". Sending a clear signal to the community is important, and futures is not stable right now.
On the other hand, I think it's perfectly fine to release some of the sub-crates as 1.x, such as futures-core, because they are a lot more stable than the futures crate.
FWIW I would expect a 1.0 release of a foundational library like futures to not see breaking changes for at least a year, unless really mandated by compiler/std changes.
This does causes extreme friction for companies.
So it definitely doesn't seem like a goal right now.
FWIW I would expect a
1.0release of a foundational library like futures to not see breaking changes for _at least_ a year, unless really mandated by compiler/std changes.This does causes extreme friction for companies.
So it definitely doesn't seem like a goal right now.
Totally agree, 1.0 should be considered as a long term support and consistent version over the time, I don't think current version should bump to 1.0, it just not ready.
That is part of what I meant by "perceptual hurdle" in the OP. IMO, a less then perfectly stable "1.0.0" is a _lesser evil_ than having the entire ecosystem have to depend on a "0.3.1", that will out of necessity end up making not-perfectly safe changes in PATCH releases (e.g. dependency and MSRV upgrades).
I would expect a 1.0 release of a foundational library like futures to not see breaking changes for at least a year
My proposal would allow/encourage a "1.1.0" release on the first MINOR compatibility change. At that point, because there is now room in the version scheme, bug fixes _could_ be back-ported (by community contributors) and released as "1.0.1" or subsequent PATCH release. Would that not satisfy your expectation, @theduke? Is that not an entirely better situation than the ecosystem depending on 0.3.x?
@dekellum That's not how semver works. If we make any breaking change, no matter how small, it must go into a major release, period. You are not allowed to make breaking changes in minor releases. That's not what minor releases are for.
You keep mentioning some sort of benefit to releasing 1.0.1 rather than 0.3.1, but there really isn't much benefit to doing so. The big distinction is between major vs non-major, the distinction between patch and minor is very very small. Cargo itself doesn't distinguish between minor and patch, they're both upgraded exactly the same, which is consistent with semver.
SemVer _requires_ putting all new features into MINOR releases, because new features have regression risk (unrealized API breakage or other bugs). I wouldn't call this a "very very small" distinction, but rather a _fundamental_ distinction which is totally missing and missed <1.0.0.
Other's have suggested bumping MINOR for MSRV and I agree and apply that approach, as pragmatic and clearly superior to the status quo of 0.x PATCH release MSRV bumps.
I have released and recommend grouping MINOR or MAJOR dependency upgrades into the same release level, in particular for public dependencies.
Rust RFC 1105: _API Evolution_ suggests many other potential MINOR as distinguished from MAJOR (or PATCH) changes.
For people that have never released anything ≥1.0.0 in an open or closed source project, there is a very strong _perceptual hurdle_ and _sanctity_ associated with 1.0.0. I again suggest getting over that ASAP, simply for the purpose of having 3 version decimals and distinguishing MAJOR from MINOR from PATCH changes.
@dekellum When using the 0.x.x versions, a 0.3.x release is a minor release, not a patch release (everything "shifts" one spot to the right), so the worst that happens is that we release some patches in a minor release. That's really not a big deal, it makes no difference in practice. The only benefit of 1.0.x is that we can make patch-only releases, which is a very minor benefit (that has no practical implications, since Cargo treats minor and patch the same).
On the other hand, releasing a new major version is a big deal, since it requires the entire ecosystem to upgrade. This is extremely painful, and takes a long time and a lot of effort from many thousands of people. Releasing new major versions on a regular basis is not good for the ecosystem, they need to be spread out to happen once every several months. This has nothing to do with "perception", it is a basic reality. There are real pragmatic costs to major version bumps.
But right now major changes are happening on a regular basis, because futures is not stable yet. So using 0.x.x communicates the fact that it is not stable and should not be used in production yet. Releasing a 1.0.0 version does not magically make it stable, instead it would be lying and pretending to be stable and production ready even though it's not.
So using 0.x.x communicates the fact that _it is not stable and should not be used in production yet_.
This is very strong message. I think the reality is not exactly like that. If that would be followed verbatim there would be no production system written in Rust at all. (Impressive amount of crates from crates.io are still at 0.x.x while being used in production massively, futures being one of them)
@imp to begin with, Cargo is 0.42.0 and Clippy is 0.0.212 :)
@imp Rust itself is 1.x, and it makes very strong stability guarantees. And there are many people who refused to use Rust before it was 1.x, for exactly that reason: Rust wasn't stable at the time. For many years Rust was at version 0.x (because it was unstable). But after years of hard work it became stable (which is why it's 1.x now).
If some people choose to take a risk and use 0.x crates in production that's their choice, but just because they choose that risk does not magically make the 0.x crates production ready. And it doesn't mean that everybody makes that choice, there are a lot of people who do refuse to use 0.x crates in production (for good reason!).
In any case, even if some other crates are production ready (and thus should be 1.x), that does not mean that futures specifically should be 1.x. Breaking changes are still happening, so no matter what your perspective on version numbers is, futures is not stable yet. This instability has nothing to do with version numbers, it has to do with the fact that the API is still evolving. Releasing version 1.x will not magically fix that. It will just send the wrong message.
At this point I'm just repeating myself, so I'll stop, but if anybody wants to argue this point they'll have to give a strong argument for why futures specifically should release 1.x right now even though it's not stable yet (and with all of the pain associated with major version changes, which I have detailed above). Other crates are irrelevant.
Of course after futures is stable then it should be released as 1.x, nobody is denying that. The question is only on what to do now, at a time when futures is not stable.
@MikailBag Cargo is shipped with Rust, so its version matches the Rust version (the same is true for clippy), so Cargo is currently at version 1.40, which makes sense because Cargo is stable. But this thread is about futures, which is not stable.
Cargo is shipped with Rust, so its version matches the Rust version (the same is true for clippy), so Cargo is currently at version
1.40, which makes sense because _Cargo is stable_.
To clarify slightly cargo the binary distributed with Rust at version 1.40 is stable and has a stable(ish) command line interface, cargo the library at version 0.42 is not stable and has breaking API changes on every release, there is a reason they have differing version numbers.
I prefer to do it in stages from more stable ones such as futures-core. see #2207 for the actual proposal to futures-core 1.0.
We plan to release it as 1.0 in order from crate, which provides abstractions (traits) such as futures-core and futures-io. We have no plans to release the utility crates and façade as 1.0 at this time, but it basically doesn't provide any additional abstraction, so I don't think it's okay.
Btw, one of the reasons why futures-* is difficult to release version 1.0 is that there are plans to add some of them to std.
Discussions about futures crate stability discussed here are for the pre-released period of futures and are quite old, so I'll close it.
If someone has any questions or opinions about the stability of individual futures-* crates, please open a new issue.
As for futures-core crate, see #2207.
Most helpful comment
FWIW I would expect a
1.0release of a foundational library like futures to not see breaking changes for at least a year, unless really mandated by compiler/std changes.This does causes extreme friction for companies.
So it definitely doesn't seem like a goal right now.