kvark/mint

The road to 1.0?

icefoxen opened this issue · 24 comments

Just wanted to foster some conversation, since mint is going into the public-facing API of the next version of ggez. The goal is to prevent problems such as this one, but with the possibility of a mint 0.6 in the works this really just moves the problem of version dependency from one place to another. mint isn't going to move as fast or change as much as nalgebra is, so really this is a problem of perception more than anything else, but having a 1.0 version might still be nice.

Any thoughts on what might need to be done for this? Or are we still at the stage of "have people use it and see if there are API problems that need fixing"?

Bear in mind that we can use the semver trick to promote a 0.x release to 1.0 at leisure without breaking compatibility.

kvark commented

@icefoxen I think getting mint used more wildly in the ecosystem in different projects is a pre-requisite for considering the 1.0 release. We need to be sure that all the bits are in place, and the only way to get there is via extensive usage.

We've just reached a point where all 3 major math libraries are compatible with (a single version) of mint, now we need to see usage. Three-rs has had it integrated for a long time, but it's not nearly as popular as Ggez. Looking forward to see how it performs :)

Makes perfect sense! Just was curious what your plans were.

It's more than 6 months later, perhaps we should revisit this.

I'd say that, for an interop library, this is very close to being all that's needed.

  • There's a few matrix types missing from the "complete" set of possible 2-4 dimensions.
  • Docs are sparse and could probably use a bit of a boost.
  • The last breaking release was Dec 2017, and the last patch release was Jul 2018. This is a good sign, it means that there's been no troubles in a fair deal of time.
  • There's always space post 1.0 if needed.

@kvark If I get a PR with some more docs and the missing matrix types would you want to move towards having a mint-1.0.0-alpha.1 in the next few months? We can announce the 1.0 alpha in the "this week in rust" newsletter and get more feedback about moving to beta and such.

kvark commented

@Lokathor

There's a few matrix types missing from the "complete" set of possible 2-4 dimensions.

Do we really need to complete the matrix type set?

The last breaking release was Dec 2017, and the last patch release was Jul 2018. This is a good sign, it means that there's been no troubles in a fair deal of time.

It's also a sign that I haven't been paying much attention or used it myself, since my three-rs was the pilot project testing it, and it was left out of the gfx (ll -> hal) conversion.

Docs are sparse and could probably use a bit of a boost.

True, and I appreciate your intent to help with them! At the same time, I'm least worried about docs, since they aren't a part of semantically versioned API.

There's always space post 1.0 if needed.

That is my main concern. Even if we agree on the current set of features being stable, it seems like moving to "1.0" would prevent us from iterating on the other features we may want to add (e.g. #16 and #32). Today, we can add these in some form in 0.5, get user feedback, and reconsider doing something entirely different for 0.6. If we transition to 1.0, there is no going to be a good way to back off the new features, and therefore there is no going to be a way to experiment with them. Unless there is some notion of an "experimental" cargo feature that obeys different semver rules from the rest of the API? Perhaps, we need to figure that out first...

Logistically, we'd also want to summon the library authors to check if they have any wishes that need to be fulfilled before 1.0. cc @nical @sebcrozet @bitshifter

Well, ripgrep is on v11, nom is on v5, it's certainly not the case that being v1 is the end of development.

experimental features can just be behind an experimental cargo flag that people opt-in to and that's clearly marked as being not part of semver.

and yeah, we might as well complete the set of matrix types, it's only a handful of extra lines of code.

kvark commented

Well, ripgrep is on v11, nom is on v5, it's certainly not the case that being v1 is the end of development.

Err... to me this is clearly the case where some projects rushed into v1 for no reason, it's a situation I specifically do not want us to end up with. I'd much rather prefer these libraries to be at 0.5 and 0.11 today.

experimental features can just be behind an experimental cargo flag that people opt-in to and that's clearly marked as being not part of semver.

Do you happen to know of a library that does that today? Would be good to have an example.

and yeah, we might as well complete the set of matrix types, it's only a handful of extra lines of code.

Are they going to be used by anybody? We can always do it later, no need to talk about this in the context of a release.

Many crates have a nightly feature which enables nightly toolchain support which, by definition, can't be considered a stable part of semver.

As to version 0.11 being better than 11.0, I don't understand how that is the case.

This might get in to version philosophy, but I would say that all that 1.0 needs to be about is that you're confident that the crate will do the job that it claims to do. If you think it can do a complete job at what it claims, and it's not going to change soon, it can be a 1.0 crate. Keeping a low version number forever and "never 2.0" and all that stuff isn't very important I think.

One thing that would have been useful to me is if mint could support transposing from column to row matrix. Sometimes in mathbench I need to compare row major libraries to column major and it would be convenient if mint could perform this conversion itself.

@bitshifter The RowMatrix4 docs, for example, have impl<T> From<ColumnMatrix4<T>> for RowMatrix4<T>, which is macro generated but sure seems to be doing the transpose.

Additional example of a lib having a stable / unstable separation:

It doesn't change the outward API, just how the library operates internally, but libm has an unstable flag that it uses.

Odd, converting from one to the other never compiled for me when I tried it, will take another look.

I would like to quote this point of semver's Q&A:

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.

For a rust library that is meant to be an integration point for a part of the rust ecosystem, I think being stable (1.0) is one of most important things to do.

Remember that rust officially recomand library authors to keep they libraries unstable as long as they have a public dependency which is not yet stable: https://rust-lang.github.io/api-guidelines/necessities.html#c-stable. And because mint is explictitly meant to be a public dependency, keeping it unstable is holding back every library using it in their API.

1.0 is not the end of development. It is only an announce that breaking changes are no longer planned.

To reply to what @kvark said:

That is my main concern. Even if we agree on the current set of features being stable, it seems like moving to "1.0" would prevent us from iterating on the other features we may want to add (e.g. #16 and #32). Today, we can add these in some form in 0.5, get user feedback, and reconsider doing something entirely different for 0.6. If we transition to 1.0, there is no going to be a good way to back off the new features, and therefore there is no going to be a way to experiment with them.

I understand your concern. However bumping from 0.5 to 0.6 it no better than bumping from 1.0 to 2.0. In both cases, that's a breaking change. It has strictly the same negative effects on the ecosystem (libraries that use mint in their API are forced to release a breaking change to update, and two libraries A and B are no longer compatible if they use different major versions of mint).

A better solution would be to use prereleases. Even after 1.0 you can implement a feature and release 1.1.0-beta.1. Then you can still break the new api (but only that) and release at 1.1.0-beta.2. Once you get confident that the API has stabilized you can finally publish the 1.1.0 version.

If really necessary, a version 2.0 can still be considered. Of course it is not desirable and we should try to avoid it. But my point is that it is not less desirable than bumping from 0.5 to 0.6.

P.S: This is also worth reading: https://github.com/dtolnay/semver-trick

kvark commented

Thank you for bringing this new information and your position @jcornaz ! I didn't know about this guideline in particular.

Remember that rust officially recomand library authors to keep they libraries unstable as long as they have a public dependency which is not yet stable: https://rust-lang.github.io/api-guidelines/necessities.html#c-stable. And because mint is explictitly meant to be a public dependency, keeping it unstable is holding back every library using it in their API.

Reading this carefully, I'd like to point out that this is an informal guideline. It doesn't block anybody from releasing a stable crate that depends on mint. In fact, there isn't even a warning, as far as I can see (just tried a dummy project with cargo publish --dry-run). So my understanding is that the authors of the guideline wanted to push the agenda of forcing 1.0 but didn't find support in Cargo authors, hence cargo doesn't enforce this.

I understand your concern. However bumping from 0.5 to 0.6 it no better than bumping from 1.0 to 2.0. In both cases, that's a breaking change. It has strictly the same negative effects on the ecosystem (libraries that use mint in their API are forced to release a breaking change to update, and two libraries A and B are no longer compatible if they use different major versions of mint).

Exactly! Bumping from 1.0 to 2.0 is no better than bumping from 0.5 to 0.6. So why making the next version special 1.0?

Because a 0.whatever version, under semver, which we all use a modified version of because that's how cargo works, is "not ready for production".

So either mint is ready for production and should be 1.0 (or more!), or mint is not ready and we should have some clear and actionable points to improve on that would make us ready.

"It might change some day" isn't actionable.

As to experimenting with optional changes, jcornaz has the right idea.

Exactly! Bumping from 1.0 to 2.0 is no better than bumping from 0.5 to 0.6. So why making the next version special 1.0?

To announce that there is no longer any breaking change planned, and that from now on, mint will try to avoid breaking changes (to stay on 1.x for as long as possible).

The question is: "is mint planning to break it's API?"

If the answer is "yes", then it should indeed stay 0.x. (but as @Lokathor, there should be a clear list of actionable tasks to achieve until we can reach the point where we no longer plan to break the API).

If the answer is "no", it should go to 1.0 to announce that. (And it can use the "semver trick" to make it as smooth as possible for the ecosystem)

Because mint wants to be part of many libraries public API's, the first priority should be to get stable.

It's fine to not be stable yet. As long as work is being done to go stable. And the first step is to define the roadmap to 1.0.

Right now, I don't even understand why we don't want to go 1.0. From your answers in this thread, I get the feeling that you don't want to go 1.0, only because you plan to continue development. But 1.0 is not the end of development. It is only the end of frequent breaking changes.

And about:

Reading this carefully, I'd like to point out that this is an informal guideline.

Yes that's true. It is nevertheless a documented best practice. And I indeed think it'd be a very bad idea to publish a 1.0 for a library that still have public dependencies on 0.x. Again 1.0 is announcing that you don't plan to break your API. But how a library can announce that they don't plan to break their api, if part of their API is a dependency that do plan breaking changes?

kvark commented

Thanks both of you for participating and trying to explain your positions!
I'm going to reply to @Lokathor comment specifically, but I think it largely answers both comments.

Because a 0.whatever version, under semver, which we all use a modified version of because that's how cargo works, is "not ready for production".

The way Rust SemVer works is different from upstream SemVer, and it precisely affects the definition of "ready for production". It's obvious why upstream SemVer requires 1.0 to be ready. But with Rust's changes, it loses any sense. I.e. Rust's fork of SemVer is begging to redefine what "ready for production" means. You can't just use the original definition in the space we are operating.

So either mint is ready for production and should be 1.0 (or more!), or mint is not ready and we should have some clear and actionable points to improve on that would make us ready.

So what exactly does it even mean to be "ready for production"? To me, it's a vague term that serves very little purpose. For example, Gecko uses a ton of 0.x.y libraries and it's shipping to 100M+ users. Is this considered production? Does it mean all those libraries need to be stable now?

All open source dependencies are largely based on trust. I use ash because I trust it to be good at avoiding unsound APIs and not breaking my code. It doesn't matter to me whether its version is 0.0.5 or 5.0.0, at all.

Similarly, I'd prefer the users of mint to trust our (developers) common sense rather than the number of the crate version.

"It might change some day" isn't actionable.

How can we be ever sure that something will not need to change? If I'm actively developing in a space that interacts with mint, over years, I may have a level of confidence that there is unlikely anything overlooked. But in the actual case, mint didn't evolve largely because I didn't look into evolving it and was busy doing other things. So even if we assume that the current code in mint is rock solid, we can't assume the same level of confidence for the new APIs we are adding. And there is a whole bunch of new APIs proposed: https://github.com/kvark/mint/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement

If we release 1.0, we'd be somewhat paralyzed in our ability to evolve the new APIs whenever we add them. I don't know what kind of changes will be needed, I'm not planning on breakage, but I also can't see the future.

We have explained how you can release a 1.0 and still experiment with the API in non-breaking ways. The standard library is an example of this.

If your standard is that you won't release a 1.0 until you can see into the future and perfectly predict that there will never be a breaking change until the end of time, then there will never be a 1.0 of mint.

If we release 1.0, we'd be somewhat paralyzed in our ability to evolve the new APIs whenever we add them. I don't know what kind of changes will be needed, I'm not planning on breakage, but I also can't see the future.

I think that's the main point I disagree with. mint can continue to evolve and see new API being added post 1.0, even if we don't know yet the kind of changes that will be needed.

If your standard is that you won't release a 1.0 until you can see into the future and perfectly predict that there will never be a breaking change until the end of time, then there will never be a 1.0 of mint.

I fully aggree with that statement of @Lokathor. And I'd like to add that it would be problematic for an interop library that aims to be part of the public API of others.

kvark commented

If your standard is that you won't release a 1.0 until you can see into the future and perfectly predict that there will never be a breaking change until the end of time, then there will never be a 1.0 of mint.

Maybe it's ok then? Why does it have to be a 1.0?
https://0ver.org/ lists a bunch of "production ready" stuff that isn't 1.0 and happy about it.

Maybe it's ok then?

For many kind of project it can be okay indeed. (Although worth documenting)

Why does it have to be a 1.0?

Because it wants to be part of the pulbic API of rust libraries.

https://0ver.org/ lists a bunch of "production ready" stuff that isn't 1.0 and happy about it.

Who's happy about it exactly? As a consumer, I am not entousiast about adding a 0.x public dependency to my library. I may do it, but I am definitely not happy about it.

Anyway I don't want to reharse old arguments. If you want to decide that mint follow ZeroVer you can (it's your project after all). I would then recomand to document that in the readme and close this issue. To at least not raise false-expectations to consumers.

kvark commented

It sounds like we arrived at a human problem here, problem of trust. There is no technical reason to go 1.0. I assume by now we can agree on it? The only reason that makes sense is basically a large population of users (or potential users) who are much concerned about the version not being 1.0.

As for the guideline, I wanted to reach out to the authors and hear them - rust-lang/api-guidelines#258

After thinking more about it, I revised my judgment. I finally somehow had a breakthrough that changes the way I think about the version number of the libraries I use and how I version my own libraries. I now disagree with my former self who commented in this thread. I am sorry @kvark if you felt pressured due to my messages. You can consider I retract all of them.

Note: My new opinion has been largely influenced by this article: https://hynek.me/articles/semver-will-not-save-you/ (although my conclusion differs slightly from the article)

With rust's variant of semver, there is no objective difference between the version 0.6.0 and 6.0.0. They both contain breaking changes (eventually aside from some features and fixes). Neither is better than the other. The only difference is the granularity of the information conveyed in that number. To quote Hynek's article:

that’s all SemVer is: a TL;DR of the changelog

With a semver number such as x.y.z where x >= 1, I can know (without reading the changelog) if it contains:

  • fixes only
  • features (maybe with fixes)
  • breaking changes (maybe with features and/or fixes)

And with a semver number such as 0.y.z, I can know (without reading the changelog) if it contains:

  • non-breaking changes
  • breaking changes.

So the only difference between a 0-based number and a non-zero-based number is the granularity of the information conveyed in the number.

Yes, many (most?) developers (incl. my former self) attribute an additional meaning to 0-based version numbers. Which is often a combination of: "is a prototype", "is not ready for production", "the API is likely to change", "the API gets breaking changes often", etc.

But I now finally realize that this meaning is far too subjective to be valuable information to convey. There are many 0-based libraries (like mint) that rarely break their API (and that's very good). And there are non-zero-based libraries (like ripgrep) that introduce regular breaking changes (and that's okay, though the fewer breaking changes, the better^^).

As a library consumer, I no longer care if the library is 0-based or not. @kvark Do whatever you please with mint's versioning ;-)

EDIT: I do care about the stability of libraries I use, especially if I put them in my public API. But I no longer infer the stability from the library's last version number.

As a library author, all my next (breaking) releases will be 1.0.0. Even if that's the first release of a brand new project. In particular, I will intentionally ignore the C-STABLE guideline. In the end, I think the only objectively useful information is what the release contains (breaking changes? features? fixes?). I do care a lot about backward compatibility. But that's a matter of trying to avoid breaking changes, not of having a 0-based version number.

EDIT: I may still use pre-releases to better convey the instability state of a new API`

So here it is. Thanks, @kvark for your answers so far. It helped me a lot to eventually reach this new mindset.

kvark commented

@jcornaz in a strangest way, your change of position and the updated reasoning is making a strongest statement for me in favor of "1.0". I've also been thinking about this in the background, the fact that "1.0" gives me a way to separate new features from patches. And as minor as it may seem, it's a good mechanism, and it would work well for both mint and other libraries I maintain, like wgpu.

With this in mind, I'll think seriously about this proposal:

As a library author, all my next (breaking) releases will be 1.0.0

And for mint, I'll consider doing the next release as "1.0". This is a bit unclear though. "1.0" would make good sense if we had breaking changes, but we aren't planning them now, at least not yet. The other option is to release "1.0" effectively re-exporting the types from the current "0.5", but I'm afraid it's a lot of movement and confusion for little benefit.