mirror of
https://github.com/semver/semver.git
synced 2026-07-11 05:12:48 -05:00
[GH-ISSUE #9] Why is it so strict about resetting minor version to zero? #874
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @vi on GitHub (Jan 4, 2012).
Original GitHub issue: https://github.com/semver/semver/issues/9
What will break if the minor version is not reset to zero while incrementing the major version number? Not resetting it will allow to preserve more or less linear flow of versions on minor (but API breaking) change.
For example, in
1.5 -> 1.6 -> 1.7 -> 2.8 -> 2.9we can talk about short "version 6" or "version 9" instead of saying the two numbers every time and psychologically rebasing the development to the beginning of major cycle ("2.0" it will seem like "completely new version with new API" rather than "next version + some API change").May be better be
SHOULD be reset to zeroorMUST be reset to zero or incremented by one?@mojombo commented on GitHub (Jan 5, 2012):
That's an interesting idea but one that's very unfamiliar. SemVer was designed to fit in well with established versioning paradigms. I think most people understand that new major versions don't necessarily mean totally new APIs, and SemVer makes it explicit that it's just a consequence of an API incompatibility. The size of that incompatibility may be large or small, it's up to the project to communicate that.
@dylanopen commented on GitHub (Jul 23, 2025):
I'm 13 years late to this. But I just wanted to add some reasons as to why I think that not resetting the minor and patch versions is actually a good idea. I think OP is completely right.
Looking at the issue number (#9) it's clear that it was realised very early on that not resetting the minor and patch versions could work.
So why do I think this is a good idea?
Not resetting any of the version segments allows you to easily tell the number of versions that have been released. If you have a project which uses this 'continuous version numbering' system, you can simply add up the major, minor and patch versions to find out exactly how many releases there have been.
It can also be used to tell how far along a project is in development. If you see something like
4.0.0, it's not clear as to whether it's had 4 released or 400, as the evidence of progress is essentially thrown away when resetting the minor and patch. Of course, project maturity shouldn't be judged based on the number of releases. But it's still a good way of telling roughly how mature a package is.As a library maintainer, I wouldn't like to go from, say, version
2.17.38to just3.0.0. Maybe it's just a psychological thing, but I think that a library would look better for users having3.17.38instead as it shows it's actually had more active development.It's easy to see exactly how many patches there have been, just look at the last number.
It's also easy to see exactly how many minor versions there have been, look at the middle number.
Most importantly, you can very easily see whether or not a library regularly introduces breaking change. If a library has been around for 5 years and has a major version of
4, it's probably more stable than one with a major version of82as it less frequently introduces breaking changes. (Before you comment, I know, you can still see this with 'resetting' semver. But most library maintainers will release breaking changes in 'big bundles' - I believe because of this notion of 'resetting the progress'. Continuous Semver, a term I will coin 😄, mostly gets rid of this issue.Why does it get rid of this issue? The biggest issue that semver has at the moment is library devs not wanting to push a new major version. They bundle up loads of breaking changes and push these all in one major release, making it really difficult for library users to update (as so much has changed).
I'm the developer of a Rust library called
Realms(https://github.com/dylanopen/realms). I use this continuous semver approach, because it allows me to push a new major version whenever I make a change, so it's really easy for my users to update (just changing a few function calls, usually).If I instead reset the minor and patch, I'd be much more weary of making major releases. That's because resetting any sort of versioning makes it feel like your losing progress. This is silly of course, but it's a real thing and the best we can do is try to make releasing major versions more normalised. Breaking change is never good, but it's much better to slowly release it rather than one huge change which makes migration near impossible.
@ljharb commented on GitHub (Jul 23, 2025):
What about prereleases? I'm not sure why it's useful to know how many releases there have been, but it's usually pretty easy to count -
npm show $package versions | wc -l, for example.These are contradictory statements.
A breaking change might be small, but also might be a complete rewrite, in which case .0.0 is the only correct choice.
The proper thing maintainers should learn from that problem, is to find ways to not do breaking changes at all, as opposed to piling up breaking changes into a big release. For example, ship the breaking behavior behind an option in a minor - then, even if you want to flip the default, the "break" is very easy to work around.
You're supposed to be :-) Breaking changes are painful and costly. They are often necessary but never ideal.
@dylanopen commented on GitHub (Jul 24, 2025):
Yes, it's obviously quite easy to count (especially with online package registries like crates.io just listing the number of versions for you). It's just personal preference: I really like my version number to represent all of the minor versions we've ever released.
Yes, when I changed my Realms backend from a pixel frame-buffer based library to an opengl-based library, I reset to version 1.0.0 as almost everything about the API had changed. So the minor versions would no longer have meaning, as they didn't refer to the current version. Maybe a better way of wording it is that, as long as the minor and patch versions relate to the current state of the library (i.e. the features and patches are still in the API) it doesn't make sense for me to reset the version, as all those patches still exist. Basically, as all the minor versions still have effect in the codebase, why make it seem as though we're throwing it all away?
I really like that idea, actually. Resetting the minor and patch when the major version changes loads of features and the API can't easily be directly translated to the newer version, but I would then keep the minor and patch version if the major version didn't break half the features.
I do agree that breaking changes should always be avoided if possible. However, as you say, breaking changes are often necessary. My main point with my reasoning was that as a library maintainer, I quite often push breaking changes which just, say, change a function to accept a reference instead of a moved value. I personally much prefer pushing one, small, breaking change as a major release, rather than changing 50 things in a single release (making migration really difficult).
In my mind, I would be more wary of pushing smaller major versions if the minor and patch reset. But this way, whenever I make a small breaking change, I release a new version, and it's really easy for my users to update.
What I meant by the issue of being wary of major versions is not that we should be pushing out as many breaking changes as we possibly can, but rather that my opinion is that it's better to release 10 small major breaking changes than one large change which causes half your codebase to be invalid. That last one is a migrating nightmare.
To be honest I really didn't expect a reply to a 13 year old post, but thanks for taking the time to respond 😆