3 ms·
OK, so unfortunately, this issue gets really tangled. I could give you a short answer, but that will invite a question. And then my answer to that question is l
by burntsushi 4y ago
OK, so unfortunately, this issue gets really tangled. I could give you a short answer, but that will invite a question. And then my answer to that question is likely to invite another. I've had this conversation many times and it always goes the same way. So I will try to anticipate those questions, but... it's subtle.
> I’ve always been a bit puzzled
I'll respond with my reasons, but I want to emphasize that I am be descriptive, not prescriptive.
The main reason why I don't just start with 1.0 out of the gate is because I generally want 1.0 to indicate some level of maturity and stability. That is, once I publish 1.0, ideally, I won't publish a 2.0. Or if I do, that timeline will be measured in years. It takes a while to get that kind of confidence with a library's API. If I had started with bstr 1.0, then this blog post would be talking about bstr 3.0. Not 1.0. Empirically, bstr 1.0 would not have been the commitment to stability that I want 1.0 to mean.
So, first question at this point is usually: well why not just increase x in x.0.0 as needed? It's okay to have 1.0 and 2.0 and 3.0. We have semver after all!
What I say to that is, yes, absolutely, you can do that. But it's absolutely a preference with respect to how often you want to release breaking change releases. My preference is to do it very rarely. Or as rarely as I can manage. The main practical reason for it is that breaking change releases create churn, and they lead to transition periods where, in the best of cases, compilation times take a hit.
For example, if I released regex 2.0, no code would break. At some point, people would start migrating to it. And for some period of time, it's likely that many projects would be building both regex 1.0 and regex 2.0 in their dependency trees. regex is not exactly lightweight, and so people are going to hunt down these issues in their trees and get everyone to migrate to regex 2.0. It's work. It's tedious. It's annoying. If I start putting out new breaking change releases of the regex crate frequently, then I'm going to annoy people in a way that is proportional to the frequency of releases. By committing to a policy that 1.0 means "I'm unlikely to publish a breaking change release for at least a few years," then that 1.0 is going to be a signal to folks that they are signing up for a dependency that is probably not going to cause them churn.
It's also especially important for bstr, because folks want to use it as a public dependency. So if I'm releasing semver incompatible releases frequently, then that's going to cause a lot of painful churn for users of bstr. It no longer just becomes a matter of compilation times. But you'll need to get your entire dependency tree migrated over, or else you risk things not working if multiple crates try to interoperate via bstr's API.
I suppose the next question at this point is, "but it's just a version number, why attach special significance to it that isn't in semver?" semver is useful for communicating breaking changes. And I think it's also useful to use the version number to communicate stability as well. But to be totally clear here: I am (EDIT) NOT trying to be an advocate here. I'm not saying this is what you or what everyone should do. There are trade offs here. I tend to build library crates that others build on, so my bias is to move slowly. But if I built crates (and I do) that are closer to the application (or even an application itself), then I'm generally much happier to just push out breaking change releases at a higher frequency.
I think the last question is, "but anything goes in 0.x.y, so says semver, so now people never know if they're getting a breaking release or not." Indeed, that is what semver says, and if that were how Cargo implemented semver, I'd probably start with 1.0 releases. (Or at the very least, publish a 1.0 release much much sooner.) But Cargo does not implement semver that way. With Cargo, 0.x.y is semver incompatible with 0.(x+1).z. That is, incrementing the leftmost non-zero digit in a version creates a new semver incompatible release from Cargo's perspective. So I get all the benefits of semver when I use 0.x.y, without needing to publish 1.0.0. The main downside is that the 'minor' and 'patch' components of the version number get collapsed into one number. But I can live with that until I publish 1.0.
- kibwen 4y ago> It's also especially important for bstr, because folks want to use it as a public dependency. Worth highlighting this part. If a crate exposes types that are likely to be exposed from the public APIs of other crates, then that changes the calculus of how disruptive a breaking change will be.
- xpe 4y agoFirst, the eternal question: Any thoughts on ways of discarding/improving semver? Second, and probably more interesting: Have you thought of some ways to formalize these notions of stability in different situations? Third, dreaming: I can imagine a space for some kind of cross-language, computer-readable description of what functions/methods/identifiers have been (i) removed; (ii) deprecated; (iii) changed; (iv) added. In some cases, such a description could be extracted by static analysis.
- burntsushi 4y ago1) No. I think semver is just fine for its intended purpose. I mean, I'm sure its spec could be improved in various ways, but its fundamental idea seems fine to me. I think it's just important to remember that semver is a means to an end, and not an end itself. It is a tool of communication most useful in a decentralized context. 2) No. 3) See: https://github.com/rust-lang/rust-semverver https://github.com/rust-lang/rust-semverver --- But also, this is only ever going to be a "best effort" sort of thing. Semver isn't just about method additions or deletions, but also behavior.