Bye Bye Semantic Versioning, Say Hello to GitDate
taylorbrazelton.com
taylorbrazelton.com
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.
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.
Semantic 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.
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:
2022.45.11+344.9c88541
2022.44.3+333.8b77462
2.1.0+303.1a65a73
2.0.3+297.3c878d2
Or without metadata: 2022.45.11
2022.44.3
2.1.0
2.0.3If 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.
Also how do you tell between types of change? Major? Minor? Patch?
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.
Yea, nah
Especially if you're using something like fzf that can easily search your command history, "every time someone has asked":
git log <tag 1>..<tag 2> --pretty=onelineHowever 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.
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)
> - 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.
> 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.
(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.)
And btw the word "inherit" in the first sentence should be "inherent."