mirror of
https://github.com/semver/semver.git
synced 2026-07-11 05:12:48 -05:00
Version incrementing with prerelease #323
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 @jadaml on GitHub (Apr 25, 2018).
I do not quite understand how the version numbers shoud be incremented when there is a pre-release node(?) id specified. The ordering of version numbers seem to indicate, that neither major, minor or patch is needed to be incremented, if a new pre-release version is released, yet I somehow feel that at least a patch number must be incremented.
So when 1.1.0-alpha is released, the next version can be 1.1.0-alpha.1 instead of 1.1.1-alpha.1
Maybe I do not fully understand how 1.1.0-alpha.1 is different to 1.1.1-alpha.1 considering that the previous version in 1.1.0-alpha. Can you clarify this for me?
@klehelley commented on GitHub (Apr 26, 2018):
I am not sure why you feel at least patch number should be incremented.
For starters,
1.1.0-alpha.1and1.1.1-alpha.1are obviously different version, and you have1.1.0-alpha.1 < 1.1.1-alpha.1. I'll try to detail as much as possible to see if it helps you wrap your head around it (which by my understanding of your post you are pretty close to doing).1.1.0-alphais a pre-release version that is directly related to a potential future1.1.0stable version.1.1.1-alpha.1is a pre-release version that is directly related to a potential future1.1.1stable version.As per the precedence rules described in part 11 of the specification, you have :
1.1.0-alpha < 1.1.1-alpha.1: not becausealpha < alpha.1but because1.1.0 < 1.1.1(the comparison stops there, no need to take the pre-release part of the version into account --1.1.0-anything < 1.1.1-anything).1.1.0-alpha < 1.1.0-alpha.1: becauseSo because of the way the precedence rules are defined, taking all the versions we use as example in both our posts, we have
1.1.0-alpha < 1.1.0-alpha.1 < 1.1.0 < 1.1.1-alpha.1 < 1.1.1.@jadaml commented on GitHub (Apr 29, 2018):
That is because I'm used another versioning scheme before where there is no pre release notation in the version number, and it would rather increase one of the numbers in a similar scenario. This is the first versioning scheme I see that notes pre releases in the version number.
But then my first hunch was correct.
I think what I miss is the management method of a pre release version of a breaking version, compared to a feature/patch release. A breaking release can have the potential of redefining the API, leaving the possible need of an in-between 0.0 version with an uncertain API-2.0.
Also it feels 0.0.0 should be treated/managed slightly differently from as 1.0.0-alpha, as the prior tells that the API still greatly changes compare to the latter, despite it not being the final either. Is that correct?
@klehelley commented on GitHub (Apr 29, 2018):
If you know for sure your next version will be breaking compatibility, then there is no issue with having a pre-release related to the expected future version (
2.0.0-alpha.1, for example).However, if you are in a situation where things aren't that clear-cut (for example with breaking compatibility not part of the plans at first, then planned after a few pre-release versions have already been delivered, to finally decide to go back to keeping compatibility...), SemVer does not provide a versioning strategy that makes things clear just by looking at the version numbers for the interface consumer. There are a couple of discussions in other issues about adding a "post-release" versioning scheme to SemVer (see #200), but they've been pretty inconclusive so far as no consensus has been reached.
Regarding the use of the pre-release format, compared to the 0.x.x scheme, the only thing SemVer says in both cases is that the compatibility is not guaranteed. But it advocates no specific scheme regarding the documenting of interface maturity. You are free to use
alpha,beta,rc... if you actually have a specific management strategy for the pre-release version and document it on the side. As long as the rules of SemVer (including precedence) are correctly followed, this is valid SemVer as far as the spec is concerned, and thus tools expecting SemVer will work correctly with the version strings you use.I personally advocate to not just rely on the version string when using SemVer (and also when not using it), be it on the provider or consumer side of the API. Sufficient documentation, in the form of changelog, release note and anything else that would help the use of the interface -- and a proper evaluation of the impact and risk associated with a version upgrade -- should be provided with any version released ; with special care for the ones labelled as stable.
@jwdonahue commented on GitHub (Jul 12, 2018):
@jadaml, you are free to increment the version numbers to reflect the relative stability of your changes. There's many ways to look at SemVer's prerelease tags:
They can be phase tags:
1.2.42-beta
1.2.43-beta
Or they can include their own counters:
1.2.42-beta.1
1.2.42-beta.2
Or you can use random alphanumerics:
1.2.42-xyz1
1.2.42-abc2
As @klehelley pointed out, each is a unique version string. As long as their is a prerelease tag in the version string, or the major version number is zero, all of SemVer's semantics around breaking change, new feature or patch, go completely out the window.
@jadaml commented on GitHub (Jul 13, 2018):
😮 Perhaps this is something that should be pointed out more clearly, as it seems to me that many may not understands this feature.
Wait. Does this means, that none of the pre-release version implementations in a SemVer based software contributes to the 'public release' on which the version number instrumentation specification correlates to?
@jwdonahue commented on GitHub (Jul 13, 2018):
Not exactly sure what you are asking there.
For the 0.y.z prerelease form:
And for prerelease tags.
Clearly, the spec allows publishers to make breaking changes with any and every prerelease. While one might think that 1.6.7-ReleaseCandidate indicates a more stable product than 1.6.7-Alpha, the presence of the prerelease tag indicates the product is not stable. The precedence rules say that ReleaseCandidate > Alpha, not because of the apparent semantic meanings of those words, but based strictly on the ASCII sort order of the tags. In terms of risk, both are equal, as are any 0.y.z or prerelease tagged publications. In other words, if it's not in the stable version form x.y.z where x > 0, with no prerelease tag, the release is potentially dangerous to production systems.
There is nothing in the SemVer spec that requires -ReleaseCandidate to be any more stable than -Alpha. Only convention and good manners suggests otherwise, so unless your tooling discriminates based on past history and other side-channel data such as publisher documentation, it can only assume there are four risk buckets:
SemVer 101, every clause in the spec has purpose. It took me one read to realize I could not grok all of it's implications, but after several passes and some thought, it is quite clear what it says.
@jadaml commented on GitHub (Jul 13, 2018):
I have read it several times my self, and for me, the more I read it, the less I understood. I still consider this, and will give it another stab.
In fact, now that I have searched normal version and see that it isn't a term that I made up, now I can rephrase better my question! I would like to reply to the rest of your reply before posting the question.
Of course! But there is no version before 0.y.z, since major version cannot be anything that by the specified values and ordering can come before 0. So in this case, I'm safe to do whatever I want. (Noted in the specification as well.)
I think the highlighted part is my answer here, but it isn't quite clear to me:
If I get over these questions however and go with the gut feeling of what this means, then these are the things I conclude, if w, x, y, z are whole non-negative numbers, and y < w:
So my rephrased questions (with reference to the specification):
When deciding whether a version number should be incremented considering compatibility (points 6., 7., and 8.), should the prerelease versions also considered (point 9.), or only the normal versions (point 2.)?
After all, ordering includes the prerelease versions between releases.
On a side note, I've just realized: Re-reading the specification again, I don't know how public API declaration in-code is meant. Is this referring to Doxygen-like comments, or like XML documentation for C#? Or perhaps is the code it self is meant to be the API declaration?
Edit: Changed x.y+1.z to x.w.0 and described w, x, y, z numbers.
@jwdonahue commented on GitHub (Jul 14, 2018):
When you put a plus sign in the middle of your version strings like that, I don't see a legal SemVer string, I see something like x.y+buildMetaBabel. It's easier for everyone if you write out the history you want to talk about in legal SemVer form. So for your "x.y+1.z-I.Troll" example I think you probably meant something like:
1.2.3-I.Troll
1.3.0-I.Troll
...
But I am not clear exactly what you were saying about them. As a consumer of versioned libraries I must treat every prerelease as potentially breaking, whether or not the publisher is trying to indicate that 1.3.0-I.Troll is just a new non-breaking feature addition in the history of the product. In fact, I expect the publisher to try to indicate that fact, because it feeds into my willingness to include it in my next test build and test production systems. While I may have decided to include 1.2.3-I.Troll in my actual production system after testing it, I would not allow an automatic update to a production system from any prerelease feeds. Depending on my history with the producer, I might not allow any auto-update of prereleases to my test beds either.
The producer/consumer dichotomy is very important for anyone working on tooling on either side of that pipeline. Where the producer says something like "bug fix prelease", the consumer hears "potentially breaking change", which is exactly the way the SemVer spec is intended to be interpreted by each side, respectively.
That last phrase is not necessarily correct. The total release history of a product is just one kind of search result data set. There are others, that do not include any prereleases, or which are limited only to a major version with a particular tag or sub-tag. The possible result sets are literally every unique combination of possible sorted subsets that can be derived from the visible history of the product. For some products, that's a huge number of possibilities. A consumer might want to do a study of the relative stability of every release version compared to; the previous N prereleases in historical order of release, or sorted by one or more prerelease identifiers. Do not attempt to merge producer and consumer business rules!
Now, finally getting to that question. The SemVer spec leaves a lot of wiggle-room for publishers to set their own policies. All eleven SemVer rules come into play, not just the few you mentioned. Here's some possible histories:
H1:
0.1.0 // Initial release to early adopters.
0.1.1 // Bug fix, I don't think I broke anything, but I did not test it.
0.1.2 // Oops! Damn customers always find my bugs before I do.
1.0.0 // I claim this is stable.
1.0.1-alpha // Another bug fix, let the customers test it.
1.0.1-beta // Maybe all I did was test it? Maybe I fixed it? You may never know.
H2:
1.0.0-1.dev // Initial release to early adopters.
1.0.0-2.dev // Bug fix, I don't think I broke anything, but I did not test it.
1.0.0-3.dev // Oops! Damn customers always find my bugs before I do.
1.0.0 // I claim this is stable.
1.0.1-alpha // Another bug fix, let the customers test it.
1.0.1-beta // Maybe all I did was test it? Maybe I fixed it? You may never know.
H1 and H2 are obviously equivalent histories to a consumer, who for the sake of this discussion, can't see the comments. Everything before 1.0.0 was clearly prerelease and that can only mean unstable to the consumer. There's a subtle difference however. The H2 publisher seems less willing to imply any intent of compatibility while H1 publisher seems to be trying to indicate some intent, but the comment indicates they did not actually apply due diligence, so why did they even bother? Now had they tested 0.1.1, their intent would have aligned with their diligence and that would be reflected in their reputation over time, so the answer to your question comes down to what is your intent and how diligent are you willing to be? Another question is, who are your customers? I've seen different policies applied to internal releases than to external releases.
The API is definitely the code, but visible comments can also be part of that interface specification, as can any documentation included with the product. From the introduction:
Lets take two hypothetical API's under consideration:
API.X 1.0.0
// Returns a number.
int GetNum();
API.Y 1.0.0
// Returns randomly distributed results using the Donahue method as documented at www.madeup.com/DonhueMehtod.
int GetNum();
Now, a user of API.X might notice that the GetNum() interface actually returns randomly distributed results that fit the same curve as the Donahue method and take a dependency on it. In fact, maybe 80% of API.X customers rely on this apparent fact. Was it part of the API? I don't think so. I could release API.X 1.0.1 that uses Juhász method instead and it's technically not a breaking change, as I can argue that the GetNum() interface could always return 1 or any other integer and it would not break the intended and stated interface. Now, I could not release an API.Y 1.0.1 based on the Juhász method without rightfully pissing off a lot of customers.
Now, if I am aware that 80% of my API.X customers depend on its close fit to the Donahue method, I probably wouldn't switch methods without issuing substantial warnings. But I might have to if someone else owns a patent on the Donahue method.
Does this help answer your questions?
@jadaml commented on GitHub (Jul 14, 2018):
With your response public API is now clear to me. It is also clear to me that it would be generally a better idea to issue a document if possible, as it is more direct.
Indeed, when I wrote x.y+1.z I meant that it is a following version of x.y.z incrementing the minor version (y). I also forgot for in the example, that in such case, the patch version should have been reset. After I post this response, I'll try to edit my response to a format that I believe is more clear; I will introduce w for y+1.
However, utilizing the same format that you have in H1 and H2 my question is more tailored toward a hypothetical version history something like the following:
H3
[...]
1.0.0 // We declare it is stable
1.1.0-dev.1 // We have some great ideas to introduce to the last API
1.1.0-dev.2 // We have introduced the ultimate feature!
1.1.0-dev.3 // We have realized that the ultimate feature is BS and quickly removed it! Luckily we want to release new features anyway, so a new release is inevitable.
1.1.0 // We declare it is stable
Question is, should the hypothetical version 1.1.0 have considered 1.1.0-dev.3's change when choosing the version number, or it did it correctly by only considering 1.0.0, but nothing in between?
Alternatively is the following hypothetical version history correct:
H4
[...]
1.0.0 // We declare it is stable
1.0.1-dev.1 // We couldn't care less what is the next normal version number is, we only know at this point, that it is a followup of 1.0.0
1.0.1-dev.2 // blah blah blah
1.1.0-dev.1 // blah blah
1.1.0-dev.2 // blah blah blah blah blah
2.0.0 // Now that we have declared it stable, we analyzed the changes we stick with at least at this point, and we came to the conclusion that this version must be versioned to 2.0.0. Or 3.0.0. Or 128.0.0 or whatever.
Could have they versioned 1.1.0-dev.2 as 2.1.0-dev.1 and get away with releasing 2.0.0 (stepping back during prerelease but before release) afterwards? Perhaps with a warning?
At this point I also wonder how these are apparent in the specification?
@jwdonahue commented on GitHub (Jul 14, 2018):
How a version considers another version? What are we talking about here? A tool of some kind perhaps? I kind of get the impression you are working on something that is supposed to decide what version of the package to generate next, but maybe you're interested in which version to consume next?
When defining user scenarios, you have to be very clear about who the user is. Remember we have consumers and producers, and depending on who you ask, you can break those two classes up into a bunch of sub-categories. The producers test automation is a kind of consumer, so it will have different rules regarding which is the next version to update to, than an end user might have, and I could go on for too long.
I think you're focused on how to go about generating the next version?
SemVer gives you lots of latitude. You can go with the target-release scheme as you seem to be doing in H2 and H3, or you can go with the monotonic-version-triple, where every publication applies the full semantic versioning scheme to the version triple, just to name two of the infinite possiblities. The later, tends to lead to faster changing version triples over time and it can be confusing to derive a real history from the visible version strings. Consider this scenario:
H5
[...]
1.0.0
1.1.0-a.dev // New feature
2.0.0-a.dev // Removed feature.
2.1.0-a.dev // Added another feature.
2.1.0-alpha // Early adopter release.
2.1.1-alpha // Bug fix.
2.1.1-beta // Wider audience.
// 2.1.1 or 1.1.0 ?
That's entirely up to the publisher! If you think about the potential consumers at each sequential publication, you are telling them exactly what to expect with regard to back-compat with the previous publication. The terminal release version in question, is not a breaking change relative to the previous release (1.0.0), so 1.1.0 would be perfectly legal as far as SemVer is concerned, as is 2.1.1.
Now I ask you to think about that, and the many other possible histories, in terms of a main release branch and a developer branch, or many branches? What developer scenarios are you trying to support? Don't ask "what can I do with SemVer?", ask "does this scenario violate any SemVer clauses?", because the set of constraints is infinitely smaller than the set of can-do's.
Also keep in mind that producers see the full history unfolding as they go, but consumers generally have a much more limited view of the historical set. For any possible producer scenario, think of the consequences of randomly removing half or more of the history, and that's typically what consumers see. In some cases, years and hundreds of publications go by before a consumer queries a feed for the available versions that meet their query criteria.
My best advice is to say what you do and do what you say, but that's a side-channel of information that SemVer doesn't cover. You should also adopt one of the packaging/publishing tool chains that supports SemVer. Nuget and NPM come to mind, but there are many others. They tend to encourage producers and consumers to use a more limited set of best practices, but with some effort, you can get out of the box. If you're developing your own tooling, get to know some of the dominant packaging tools anyway,
@jwdonahue commented on GitHub (Oct 11, 2018):
@jadaml, if you have no further questions, please close this issue at your earliest possible convenience.
@jadaml commented on GitHub (Oct 14, 2018):
Yes, of course. Sorry.