I like this, I've already started versioning with the (2 digit lol) year as major version so 22.19.73 is the current year/release/build, but a standardized thing like this would be great. When versions gets old the version numbers start to add up you might as well use the year, ie Iphone 14, you might as well call it 22.
Plus, if your main issue with semantic versioning is that you cant find your changelog, make it part of your git repo. At BeamMP (a multiplayer game project) we have a changelog.md file right in the server repo. Go to any release tag and youll automatically also have the relevant changelogs.
I agree. Additionally, some binaries like .NET assemblies have "informational" versions that can be set to arbitrary strings. I like to version my assemblies something like 1.2.3.4-rc.1+githash to communicate not only the semantic version but also the commit hash used during the build.
We use this. Caveat: date is not the day of release if you are doing stuff in the “easy way” and aren’t full CD to prod. So your release on the 23rd could be something packaged the 21st
This basically doesn’t matter though, and is easy, and avoids most ordering questions as well that come with CI.
Highly recommend, and at least think semver is silly for versioning applications in general (libs are different of course)
No, don't do this. You're concerned about information loss, but without semver the consumer doesn't know if a major change occurred that would break their builds.
Software vendors and consumers do NOT want automatic major version bumps on Jan 1st. They DO want control over when a major version is released and when they update to it. Using 2022-06-11 as your version, do you hold all your major breaking changes for Jan 31st? That's impossible.
You get all those "Pros" by working within Semver and appending the date to the patch part: major.minor.patchyearmonthday
Edit: To support version parsers always expecting only 3 version numbers I removed the dashes in the example.
Note that if you release something like 2021.x.x on an App Store you’ll never be able to revert back to a number like 3.x.x. So make sure that’s the direction you want to go.
Strong disagree with this post. Semantic versioning means: MAJOR version when you make incompatible API changes, MINOR version when you add functionality in a backwards compatible manner, and PATCH version when you make backwards compatible bug fixes.
The purpose is to enable APIs and build tools to work well, such as package manager project files that specify a version.
To answer the author's questions:
"When was the last time you could tell someone the difference between version 1.2.1 and 1.2.3 quickly?" I can tell it's a patch upgrade, so my team's projects can do this upgrade automatically, with tests of course. Also, I can look at the source git repo, or changelog, or equivalent.
"Can you tell me when version 1.2.1 was released?" Yes, by looking at the source git repo, or git tags, or changelog, or equivalent.
"What is your company/team's meaning behind a major, minor, and patch increment?" Semver specifies how to do this in the first paragraph of www.semver.org.
"Who decides when a new major version is released?" The person who is responsible for the API breaking change; depending on the team and goals, this person could be the developer, or project manager, or release manager, etc.
"What happens to the version where the manager or developer forgot to put together a change log?" We test for it in our git pre-commit hook and in our continuous integration. If your team isn't using those, or equivalents, then simply add a changelog and push a patch version.
The author seems to be focusing on applications. If you’re releasing an app, service or anything else that doesn’t expose a public API, there is no point in having those compatibility semantics.
If you're releasing an app that our teams are using, then yes please use semver, because we manage apps via system package managers (e.g. macOS brew, Windows wpm, Ubuntu apt, etc.) and containers (e.g. Docker, Podman, etc.), and infrastructure as code (e.g. Ansible, Packer, etc.).
This enables our systems to automate our application updates. We do PATCH updates continuously (e.g each night), MINOR updates during system downtime (e.g. each weekend), and MAJOR updates with manual scheduling when we can look at the changelogs.
If you're not managing your systems' applications with package managers or containers or infrastructure as code, then IMHO these are well worth a look.
No, because the functionality is the same. Just because a test may be so brittle that it breaks the test does not change the meaning (it just may mean you have to manually inspect the now failing test).
The point is that most patch upgrades can just be run through a test suite and accepted. That may still lead to breakages, something that seemed minor and non-breaking to the developer could instead be breaking for a given user, but the point of semver is to convey actionable meaning and intent, which it does.
Yes. Also several weeks of beta are required before releasing the change, and a few blog posts about how a talented team at $COMPANY deals with challenges of UI design.
If the application documentation promises a stable screenshot, but an update moves a button, then yes this is a breaking change.
This comes up in the real world sometimes such as with automatic testing scripts that use absolute cursor positioning, as well as in some kinds of compliance where any graphic change requires a compliance verification of the new UI.
The first two responses to you were a solid yes and a solid no. I think that just demonstrates how the concept does not apply to application versioning.
It most certainly applies, the previous poster gave good examples of config files syntax and semantics, db compatibility, etc, etc.
On the UI front it really depends, you need to make decisions and let users know to avoid surprises.
At Sun the process was that every possible interface that something or someone can depend on is acknowledged to be an interface. Is GUI pixel positioning an interface? Well yes, clearly it is as someone could write tools that rely on that position.
But do you as the author want to give users an assurance that you won't break this except in major releases? Maybe not. So interfaces were classified on the axis of public/private and stable/unstable (it was a bit more complex but that's the gist). Most applications used to classify the GUI elements as an unstable interface. Then you document that so consumers of the application know they shouldn't rely on GUI elements not moving.
I was specifically trying to contrast programming interfaces against the large universe of applications that don’t expose any. You bring up an interesting point about external dependencies such as db, however I would say the vast majority of these applications I refer to don’t rely on users to set these up.
Even if you do have an API, sometimes you have versioning built into that API anyway, e.g. in openapi and gRPC (depending on how you structure your protobuf files). At that point, a date-based version starts looking more reasonable.
Agreed. And even more so if you're developing a SaaS product or otherwise hosted app which is continuously updated.
The closer you get to continuous delivery, the more date-based versioning can make a bit more sense as opposed to "traditional" semantic versioning (though it's still often a subjective decision).
No, they still want to move quick, and you can do that by relying on the change signaling of your vendor: if you know the vendor is trustworthy, you can deploy minor updates with only minimal testing. If the vendor is unreliable or doesn't signal their changes accurately, every "minor" update becomes a liability, and that's where it gets slow.
Companies that want to move quickly should not use unreliable vendors.
Breaking things is bad. Despite what Mark says. When you break things companies lose customers, they lose money, they lose market share, and people get fired or die.
Any bugfix could be seen as backwards incompatible in the strict sense "somewhere, someone might actually depend on this buggy behavior", yes. That’s not a particularly good definition of backwards incompatibility when it comes to bugfixes, however. A more reasonable definition is "requires changes to downstream code" as in the bug cannot be fixed without changing public API as well. That said, sometimes there definitely exist bugs that a lot of users factually rely on, and changing such behaviors should not be done in patch releases.
Which is why you provide patch notes, and why the more conservative consumers won't even auto-accept patch updates. The point is that a bug fix that doesn't change the API, and which only changes behavior to match intent and documentation, is something that requires minimal testing by the user.
I.e., "fixed possible race condition where..." is a patch.
It may be that a consumer relied on a particular bug when developing, yes, and that fixing it now 'breaks' their application, but that's a definite minority, and why you communicate out the fixes (and for auto-adopting patch releases, you ideally still have automatic tests to validate).
> What even is a 'backwards compatible bug fix'? Unless it's performance, isn't a bug-fix by definition not going to be backwards compatible?
It's a fix that makes the code work as documented, which it didn't do before, ergo had a bug.
To pick a random example, the man page for strlen on Linux reads:
"The strlen() function calculates the length of the string s, excluding the terminating null byte ('\0')."
If it behaves differently (let's say it counts the terminating null) then strlen has a bug. The fix to that can go into a patch release because it simply fixes the interface to work as documented.
You could go down a rathole of saying well maybe someone depends on the broken functionality. Sure, but that's not what the documented contract was. The documented functionality is the contract the API needs to meet.
That's too-specific an example. Often the bug is a corner case which wasn't considered, so that the documentation didn't say what was happening, and people were implying on the old relying behaviour.
It's OK if systems are imperfect, they are still helpful even though exceptions may occur. Intent matters. You may even see backwards incompatible changes treated as bugs fixed in subsequent releases.
Almost all code is aspirational in terms of matching a spec, being backwards compatible, not having bugs, etc. Its meaningless to talk about what is guaranteed with respect to practically every piece of software in existence. What is important to a developer is "what is the risk that upgrading this software will break my deployment, and what do I gain from upgrading it", which semver relays to the developer to the best of the library author's ability.
Functionality can always be added in a backward compatible manner, that is indeed the purpose of a minor release.
If libfoo has two public interfaces, sing() and dance(), I can add a third interface walk() and publish a binary-compatible libfoo.so which your consuming code can link to without any concern, as your code isn't calling walk() before it existed.
> The moment you allow bugs, you can't ensure backwards compatible changes.
I'm not sure what this even means, unless you're talking of the joke of "someone depended on the bug"?
A bug, by its very definition, means code that doesn't behave as the documented interface description states. Fixing bugs means correcting mistakes in the code so that now it does behave as documented. That's what patch releases do.
Are you referring to the possibility of a minor release introducing a bug?
Yes, of course that's possible, nobody is pretending humans are infallible. When it happens, just issue a patch release to that minor release to fix the bug. Versioning is about communicating intent, it's not a guarantee that nobody can ever make a mistake.
But if you're constantly introducing bugs in minor releases, that's a sign of sloppy test infrastructure and it will motivate me to migrate away from such a library. But the occasional bug is normal, fix it quick and move on.
Strongly agree with your strong disagreement. The blog author doesn't seem to have any experience releasing software that others need to actually rely on.
I would not want to have to depend on any library this blog author releases. From that list of random meanigless dates, how would tools know which release is a compatible bugfix and which ones carries breaking changes?
The assumption there seems to be that release date is the one and only thing consumers of the release care about. But as a consumer of some library, I can tell that I mostly don't care at all about the release date. But I must know what the release contains in terms of change disruption, which is the purpose of versioning.
TBH the OP post seems to be highlighting that "when we forget history, we're doomed to repeat it."
Not trying to be ageist but a younger dev wouldn't have context on a time when the industry had timestamps as versions and the core reason why SemVer became preferred, as you describe above.
i’ll answer some questions from the article based on my own experience:
> When was the last time you could tell someone the difference between version 1.2.1 and 1.2.3 quickly?
patches mean small bug fixes or improvements that don’t break the api.
> Can you tell me when version 1.2.1 was released? Was it yesterday? Last year? Ten years ago?
for this we’ll have to check the repository. does it matter thou? if the package is stable then it doesn’t. it also depends on the platform. my golang projects rarely get updates. my javascript projects get updates once a week.
> What is your company/team's meaning behind a major, minor, and patch increment?
patch - small changes, no api changes
minor - medium changes, no api changes
major - big changes, api changes
> Who decides when a new major version is released? Is it a person? Maybe it's based on the size of an architectural change?
no, it’s if the api changes.
> What happens to the version where the manager or developer forgot to put together a change log?
But please don’t use CalVer’s so-called “standard terminology”, because having “MM” be 1–12 and “0M” 01–12 (and the same for D and mostly Y) is ridiculous, flying in the face of the extremely well-established society-wide convention that “MM” means 01–12, and “M” 1–12.
(Because of this folly, I firmly decline to cite CalVer in anything about date-based version numbers. It’s an obvious enough scheme anyway and doesn’t need a fancy name or specificationy sort of thing.)
I think this is ok for software with linear releases, for example closed-source SaaS applications. You can easily see the date of the release and compare 2 versions.
However a lot of software, especially in open source, does not follow linear releases. Let's say I use SemVer and I fix a bug in versions 1.2.0 and 2.3.0 of my library. I release Release 1.2.1 is created after 2.3.0, but I can easily immediately see it is not a continuation of 2.x branch. With GitDate, my bug fix will be called 2022.06.11-123abc, and it's hard to see that it's not a continuation of 2022.06.09-abc123 release, but an old 2021.11.12-def456 version.
This kind of sounds like "I choose semantic versioning but actually I didn't really need semantic versions, so now I just have a dated build number"
It's fine, but its not very useful in many different kind of situations. Like for instance managing firmware versions where you have a version 5 which is the last version compatible with a certain level of hardware and you need to hot fix it, while the most "modern" version is version 9.
But if you just keep releasing something like a website, then yeah, all you need is a build number, the date might be helpful, but most likely you don't care too much about that either.
Not all version storage mechanisms support non-numeric components. For example, Windows executables' file version values can only be up to four dotted number components whose values cannot exceed 65535.
If the package manager for the system lacks sufficient information regarding any system component, that would seem grounds for switching to a different OS.
No, this is no good. Imagine trying to discuss the version with a colleague. We can all remember 2.1.3 but we can't remember the git short code and date.
If you're working on the project then you'll have a link to the repo or docs so it should be trivial to look up what changed based on the semVer change.
Date versioning is great. Just look at all the success of windows 95, 98, 2000 … oh wait. Let’s just give them cute names like xp, vista … oh wait. Everybody understands increasing version numbers. And semver provides more information at a glance than date+hash - that’s probably fine for nightly test builds but not for production releases.
Many of the disagreements in the comments seem to be overly focussed on versioning for installable software and dependencies (for example packages) where, I would agree, semantic versioning is still useful.
Not all software is a dependency though, and not all software is installed by end users. The approach in this article is fine for hosted apps, or anything that's at the top of the dependency chain.
EDIT: That said, this scheme is missing a sortable differentiator for multiple releases on a given date. This means there is no way to calculate the order of precedence between multiple releases on the same day, which is often important (though may not be in the author's specific case). I recently switched to using a hybrid version number which has elements of date based and semantic versioning (though not really "semantic" in the traditional sense):
2022.45.11+344.9c88541
2022 = Current year
45 = Release number in the current year
11 = Changes in this release (this is a count of Git commits on the release branch since the last tagged release)
Metadata:
344 = Unique build number
9c88541 = Git hash
This is completely automatically generated and each version is tagged automatically in the repo on deployment. So I never have to think about versioning at all.
I only use this for top-level applications (SaaS/hosted) which are continuously delivered (often with multiple deployments per day) and have no downstream dependancies.
I used to use Semver 2.0, so in my new hybrid scheme I wanted my version numbers to still have the same number of elements and still be sortable in a list with my older Semver 2 version numbers.
Given that my major versions never got anywhere close to 2022, this format gave me a nice seamless switchover, where order of all version numbers across both schemes was still maintained and my version numbers still had a consistent number of elements (important for my CI tooling):
Eg. versions, in order of most recent, still sortable across versioning format change:
It could be useful, I am working on a project now I think this would be a better fit. I don’t think it would replace semantic versioning for all projects though.
How does one release/run two parallel branches at once?
If I'm running version X.y, and (X+1).0 is released, but it's new and has a whole bunch of changes, I may not want to move to it right away. I can do that with SemVer since two parallel streams are possible.
73 comments
[ 3.5 ms ] story [ 66.1 ms ] threadSemantic versioning is about compatibility and amount of changes, sort of. I know that going from 3.0.1 to 3.1.0 wont break the API.
Not once have I been asked when 3.0.1 was released - and if Im asked, I can look at the release posts, or anything else.
Cute idea, but appending a git commit hash to the end of a semantic version may be more useful than any of this.
This basically doesn’t matter though, and is easy, and avoids most ordering questions as well that come with CI.
Highly recommend, and at least think semver is silly for versioning applications in general (libs are different of course)
Software vendors and consumers do NOT want automatic major version bumps on Jan 1st. They DO want control over when a major version is released and when they update to it. Using 2022-06-11 as your version, do you hold all your major breaking changes for Jan 31st? That's impossible.
You get all those "Pros" by working within Semver and appending the date to the patch part: major.minor.patchyearmonthday
Edit: To support version parsers always expecting only 3 version numbers I removed the dashes in the example.
The purpose is to enable APIs and build tools to work well, such as package manager project files that specify a version.
To answer the author's questions:
"When was the last time you could tell someone the difference between version 1.2.1 and 1.2.3 quickly?" I can tell it's a patch upgrade, so my team's projects can do this upgrade automatically, with tests of course. Also, I can look at the source git repo, or changelog, or equivalent.
"Can you tell me when version 1.2.1 was released?" Yes, by looking at the source git repo, or git tags, or changelog, or equivalent.
"What is your company/team's meaning behind a major, minor, and patch increment?" Semver specifies how to do this in the first paragraph of www.semver.org.
"Who decides when a new major version is released?" The person who is responsible for the API breaking change; depending on the team and goals, this person could be the developer, or project manager, or release manager, etc.
"What happens to the version where the manager or developer forgot to put together a change log?" We test for it in our git pre-commit hook and in our continuous integration. If your team isn't using those, or equivalents, then simply add a changelog and push a patch version.
This enables our systems to automate our application updates. We do PATCH updates continuously (e.g each night), MINOR updates during system downtime (e.g. each weekend), and MAJOR updates with manual scheduling when we can look at the changelogs.
If you're not managing your systems' applications with package managers or containers or infrastructure as code, then IMHO these are well worth a look.
The point is that most patch upgrades can just be run through a test suite and accepted. That may still lead to breakages, something that seemed minor and non-breaking to the developer could instead be breaking for a given user, but the point of semver is to convey actionable meaning and intent, which it does.
This comes up in the real world sometimes such as with automatic testing scripts that use absolute cursor positioning, as well as in some kinds of compliance where any graphic change requires a compliance verification of the new UI.
On the UI front it really depends, you need to make decisions and let users know to avoid surprises.
At Sun the process was that every possible interface that something or someone can depend on is acknowledged to be an interface. Is GUI pixel positioning an interface? Well yes, clearly it is as someone could write tools that rely on that position.
But do you as the author want to give users an assurance that you won't break this except in major releases? Maybe not. So interfaces were classified on the axis of public/private and stable/unstable (it was a bit more complex but that's the gist). Most applications used to classify the GUI elements as an unstable interface. Then you document that so consumers of the application know they shouldn't rely on GUI elements not moving.
- changes in configuration statements
- changes in configuration defaults
- removal of functionality
- file format changes
- major architectural overhaul (maybe the functionality is still the same, but you still want to verify that yourself before deploying)
User acceptance testing is not just for software manufacturing, it can be done wherever the users are, and some companies take this very seriously.
The closer you get to continuous delivery, the more date-based versioning can make a bit more sense as opposed to "traditional" semantic versioning (though it's still often a subjective decision).
Companies that want to move quickly should not use unreliable vendors.
Breaking things is bad. Despite what Mark says. When you break things companies lose customers, they lose money, they lose market share, and people get fired or die.
What even is a 'backwards compatible bug fix'? Unless it's performance, isn't a bug-fix by definition not going to be backwards compatible?
I.e., "fixed possible race condition where..." is a patch.
It may be that a consumer relied on a particular bug when developing, yes, and that fixing it now 'breaks' their application, but that's a definite minority, and why you communicate out the fixes (and for auto-adopting patch releases, you ideally still have automatic tests to validate).
It's a fix that makes the code work as documented, which it didn't do before, ergo had a bug.
To pick a random example, the man page for strlen on Linux reads:
"The strlen() function calculates the length of the string s, excluding the terminating null byte ('\0')."
If it behaves differently (let's say it counts the terminating null) then strlen has a bug. The fix to that can go into a patch release because it simply fixes the interface to work as documented.
You could go down a rathole of saying well maybe someone depends on the broken functionality. Sure, but that's not what the documented contract was. The documented functionality is the contract the API needs to meet.
Impossible to guarantee and thus impossible to do SemVer.
If libfoo has two public interfaces, sing() and dance(), I can add a third interface walk() and publish a binary-compatible libfoo.so which your consuming code can link to without any concern, as your code isn't calling walk() before it existed.
That makes semver as good as any other scheme, including GitDate.
I'm not sure what this even means, unless you're talking of the joke of "someone depended on the bug"?
A bug, by its very definition, means code that doesn't behave as the documented interface description states. Fixing bugs means correcting mistakes in the code so that now it does behave as documented. That's what patch releases do.
Yes, of course that's possible, nobody is pretending humans are infallible. When it happens, just issue a patch release to that minor release to fix the bug. Versioning is about communicating intent, it's not a guarantee that nobody can ever make a mistake.
But if you're constantly introducing bugs in minor releases, that's a sign of sloppy test infrastructure and it will motivate me to migrate away from such a library. But the occasional bug is normal, fix it quick and move on.
This dovetails nicely with the discussion yesterday on using dependencies (https://news.ycombinator.com/item?id=31692606).
I would not want to have to depend on any library this blog author releases. From that list of random meanigless dates, how would tools know which release is a compatible bugfix and which ones carries breaking changes?
The assumption there seems to be that release date is the one and only thing consumers of the release care about. But as a consumer of some library, I can tell that I mostly don't care at all about the release date. But I must know what the release contains in terms of change disruption, which is the purpose of versioning.
Not trying to be ageist but a younger dev wouldn't have context on a time when the industry had timestamps as versions and the core reason why SemVer became preferred, as you describe above.
> When was the last time you could tell someone the difference between version 1.2.1 and 1.2.3 quickly?
patches mean small bug fixes or improvements that don’t break the api.
> Can you tell me when version 1.2.1 was released? Was it yesterday? Last year? Ten years ago?
for this we’ll have to check the repository. does it matter thou? if the package is stable then it doesn’t. it also depends on the platform. my golang projects rarely get updates. my javascript projects get updates once a week.
> What is your company/team's meaning behind a major, minor, and patch increment?
patch - small changes, no api changes
minor - medium changes, no api changes
major - big changes, api changes
> Who decides when a new major version is released? Is it a person? Maybe it's based on the size of an architectural change?
no, it’s if the api changes.
> What happens to the version where the manager or developer forgot to put together a change log?
we use git’s history for this.
https://calver.org/
(Because of this folly, I firmly decline to cite CalVer in anything about date-based version numbers. It’s an obvious enough scheme anyway and doesn’t need a fancy name or specificationy sort of thing.)
Also how do you tell between types of change? Major? Minor? Patch?
Especially if you're using something like fzf that can easily search your command history, "every time someone has asked":
However a lot of software, especially in open source, does not follow linear releases. Let's say I use SemVer and I fix a bug in versions 1.2.0 and 2.3.0 of my library. I release Release 1.2.1 is created after 2.3.0, but I can easily immediately see it is not a continuation of 2.x branch. With GitDate, my bug fix will be called 2022.06.11-123abc, and it's hard to see that it's not a continuation of 2022.06.09-abc123 release, but an old 2021.11.12-def456 version.
It's fine, but its not very useful in many different kind of situations. Like for instance managing firmware versions where you have a version 5 which is the last version compatible with a certain level of hardware and you need to hot fix it, while the most "modern" version is version 9.
But if you just keep releasing something like a website, then yeah, all you need is a build number, the date might be helpful, but most likely you don't care too much about that either.
> - Multiple builds on the same day? You would need access to the git repo to figure out which came first.
> - Customers now have visibility into the last time their software was updated.
- If corporations like Apple don't support it, the whole thing goes down the drain.
- I know this seems crazy, but I have met people that don't use Git for version control.
If you're working on the project then you'll have a link to the repo or docs so it should be trivial to look up what changed based on the semVer change.
Not all software is a dependency though, and not all software is installed by end users. The approach in this article is fine for hosted apps, or anything that's at the top of the dependency chain.
EDIT: That said, this scheme is missing a sortable differentiator for multiple releases on a given date. This means there is no way to calculate the order of precedence between multiple releases on the same day, which is often important (though may not be in the author's specific case). I recently switched to using a hybrid version number which has elements of date based and semantic versioning (though not really "semantic" in the traditional sense):
Metadata: This is completely automatically generated and each version is tagged automatically in the repo on deployment. So I never have to think about versioning at all.I only use this for top-level applications (SaaS/hosted) which are continuously delivered (often with multiple deployments per day) and have no downstream dependancies.
I used to use Semver 2.0, so in my new hybrid scheme I wanted my version numbers to still have the same number of elements and still be sortable in a list with my older Semver 2 version numbers.
Given that my major versions never got anywhere close to 2022, this format gave me a nice seamless switchover, where order of all version numbers across both schemes was still maintained and my version numbers still had a consistent number of elements (important for my CI tooling):
Eg. versions, in order of most recent, still sortable across versioning format change:
Or without metadata:If I'm running version X.y, and (X+1).0 is released, but it's new and has a whole bunch of changes, I may not want to move to it right away. I can do that with SemVer since two parallel streams are possible.