Skip to content
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

Consider releasing as 1.0.0 #1934

Closed
dekellum opened this issue Nov 1, 2019 · 21 comments
Closed

Consider releasing as 1.0.0 #1934

dekellum opened this issue Nov 1, 2019 · 21 comments

Comments

@dekellum
Copy link
Contributor

dekellum commented Nov 1, 2019

Motivation

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.

Perceived Drawbacks

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.

Further Reading

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.

@Pauan
Copy link

Pauan commented Nov 1, 2019

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.

@dekellum
Copy link
Contributor Author

dekellum commented Nov 2, 2019

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.

@Pauan
Copy link

Pauan commented Nov 3, 2019

@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.

@taiki-e
Copy link
Member

taiki-e commented Nov 3, 2019

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.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.

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.)

@Pauan
Copy link

Pauan commented Nov 3, 2019

@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.

@Nemo157
Copy link
Member

Nemo157 commented Nov 3, 2019

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.

@dekellum
Copy link
Contributor Author

dekellum commented Nov 3, 2019

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.

@Pauan
Copy link

Pauan commented Nov 4, 2019

@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.

@theduke
Copy link
Contributor

theduke commented Nov 8, 2019

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.

@CGQAQ
Copy link

CGQAQ commented Nov 25, 2019

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.

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.

@dekellum
Copy link
Contributor Author

dekellum commented Nov 25, 2019

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?

@Pauan
Copy link

Pauan commented Nov 25, 2019

@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.

@dekellum
Copy link
Contributor Author

dekellum commented Nov 25, 2019

  • 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.

@Pauan
Copy link

Pauan commented Nov 26, 2019

@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.

@imp
Copy link
Contributor

imp commented Nov 26, 2019

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)

@MikailBag
Copy link

@imp to begin with, Cargo is 0.42.0 and Clippy is 0.0.212 :)

@Pauan
Copy link

Pauan commented Nov 26, 2019

@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.

@Nemo157
Copy link
Member

Nemo157 commented Nov 26, 2019

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.

@taiki-e
Copy link
Member

taiki-e commented Sep 4, 2020

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.

@taiki-e
Copy link
Member

taiki-e commented Feb 21, 2021

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.

@taiki-e
Copy link
Member

taiki-e commented Feb 21, 2021

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.

@taiki-e taiki-e closed this as completed Feb 21, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants