Where i work, most of our libraries have a version number with a single component, which we increment with each release. So i recently released a new version of AdminTools, taking it from 37 to 38. Should i go and register SerVer.org so i can tell the world about this incredibly trivial scheme?
If it spreads the idea that there are several ways to do versioning, and not only semver, and it gives a proper rationale, I'm ok with that. (server.org might be difficult to register though).
CalVer is just a pattern with a name so you can talk around about why Ubuntu uses year.month as version, and some ideas on when it is good to use it or not.
Yes. I've got some questions about your incredibly trivial scheme:
* Do users declare a dependency on "= 37", or ">= 37"? (Or do they have the option of either?)
* How do you push backwards-compatible changes like bugfixes? Is the rule that every new release is a new version number?
* If you do the >= thing, do you automatically take the newest compatible dependency or the oldest possible one (vgo-style)? Do you run tests on reverse dependencies when dependencies are updated?
* Are you allowed to have diamond dependencies, e.g., A depends on B and C, B depends on AdminTools 37, and C depends on AdminTools 38? Do you resolve that with a single copy of AdminTools, or two?
> Do users declare a dependency on "= 37", or ">= 37"? (Or do they have the option of either?)
"= 37".
> How do you push backwards-compatible changes like bugfixes? Is the rule that every new release is a new version number?
A backwards-compatible change gets a new version number, like any other change.
The system does actually let you use version numbers like "37.1", but it doesn't assign any meaning to them. They're all opaque symbols, really. You could just as well have versions "first", "new", "improved new", "new new", "new new (final)", "new 2", etc.
> If you do the >= thing, do you automatically take the newest compatible dependency or the oldest possible one (vgo-style)?
We don't use ">= 37".
> Do you run tests on reverse dependencies when dependencies are updated?
Oh god no.
Personally, when i'm upgrading a library, it will usually be to address a need of a particular consumer, and i will develop the upgrade and the change to the consumer in concert. So that's sort of testing it with a reverse dependency, but only one.
> Are you allowed to have diamond dependencies, e.g., A depends on B and C, B depends on AdminTools 37, and C depends on AdminTools 38?
You can express a dependency like that, and if there is a conflict, the package manager shouts and you and stops. You then have to write something like "no really, use version 38" to persuade it to work.
> Do you resolve that with a single copy of AdminTools, or two?
A single copy.
To my mind, these are all questions about our package management tool, not our versioning scheme.
Differently in different projects, but usually adopting CalVer is a sign that, unlike with SemVer, you're not using the version number to connote backwards compatibility. Code that continues to be accessible via the same path/name/etc. is always backwards-compatible (i.e., if a consumer application would compile or run fine on a newer version without errors, there aren't hidden semantic changes). If you need to make a backwards-incompatible change, you handle it in some means other than the version number, usually either by renaming the project, the import path, or the function.
Looking at the example CalVer projects: Ubuntu follows this model because it's the model of basically all UNIX distributions, that a particular command name doesn't change meaning / API without either a deprecation cycle or an explicit action from the sysadmin. If a command changes incompatibly (e.g., Python 2 to Python 3), it gets a different name (e.g., /usr/bin/python to /usr/bin/python3). If a library changes incompatibly, it either uses symbol versioning infrastructure to provide old and new versions of the function (e.g., fmemopen@GLIBC_2.2.5 is now provided by libio/oldfmemopen.c, and the fmemopen@GLIBC_2.22 implementation, which fixes tons of bugs and drops questionable features, is only visible to programs compiled on a system with glibc 2.22 or newer), or the library itself has a new filename (e.g., at some point libcurl.so.3 became libcurl.so.4). Therefore, code that compiled and ran fine on, say, Ubuntu 16.04 should generally be expected to compile and run fine on Ubuntu 18.04, or less commonly, not run at all with a clear error up front about the specific missing functionality.
Twisted, youtube-dl, and pytz follow a similar model. Code written against them years ago should generally continue to work; the version number provides information on new features, not on deprecations.
Teradata UDA is different, but it follows the underlying CalVer philosophy of breaking changes being something to avoid. (But it uses a year and month to indicate an API compatibility level, which isn't how the rest of the CalVer projects use dates.)
So the answers to my bullet points for CalVer are generally ">=", "Rename the functionality, do not ever redefine an existing name", "Newest compatible dependency is fine and you should definitely have integration tests," and "Diamond dependencies to different versions aren't a problem, diamond dependencies to different names e.g. libcurl.so.3 and libcurl.so.4 are discouraged but should work".
A version number which includes the date it is released is more common for development or “snapshot” releases, so you should only use this versioning scheme if you are fine with people assuming it is in fact that kind of interim or unofficial release.
Take for instance Ubuntu - should those releases be considered unofficial? [no]
How about the python-attrs library - should those be considered interim? [no]
All versioning schemes fail somewhere and CalVer and SemVer (and certainly others) are not exceptions to this rule. The problem is you’re reducing a ton of metadata into a human-readable, compact label - and that’s inherently lossy.
I was interested to see a more formal definition of calendar versioning than I've come across before, until I saw that it refers to `DD` for short day while `0D` is zero padded - which goes against all date formatting specifications ever... `DD` should be zero-padded and a single `D` unpadded...
I think office is a bit different as it is an end user software rather than a library. Operating systems (especially Windows) have very good backwards compatibility so there is usually not much need to be able to specify a compatible version beyond the major release number/name.
Also missing: every Adobe product since the move to a subscription model. Sorta. They still retain an internal sequential version number; Illustrator CC 2018 = 22.0.0, for instance.
Are they trying to make this as obscure as possible? After reading the top of the scrollable page, I still couldn't understand what a CalVer version string looks like.
Also, I think version strings should contain information about compatibility, not just about time.
Web server version may talk about supported config syntax, supported out of the box features and modules. Anything not backward compatible should bump major version.
Not much project would benefit from this kind of versioning. Only websites, desktop apps with no config files, etc.
This makes a lot of sense for a library like pytz, which provides an interface to constantly-changing data; you don't want the version to indicate APIs, you want it to indicate the time of last data update.
Has anyone successfully transitioned from SemVer to CalVer? Especially with a large customer base who is familiar with the the major product versions but gets confused with the minor/patch variants and what’s included ?
It seems very odd, to me, to make this a variable-length field. If you're planning out to 2106, why would you not use a full length year? This particular bike shed should clearly be blue.
I decided to use calendar versioning for our companies internal ERP system since it makes it explicit to end users when a new release has been rolled out (2018-06-06.1 is more human than #9b867341 or 27.1.321.1).
No reason other than that.
I also change the background image of the login page on 'major' releases where significant new features are added as a subtle visual cue that users should go check out the change log.
It's often the little touches that have a disproportionate impact on user engagement.
CalVer is also nice for mature projects that don't get new groundbreaking features because they don't need them.
I wrote a small piece of code in my project's setup.py [1] that computes the version number from the timestamp of the last git commit. It's very useful because it removes the need to make a commit for the sole purpose of updating the version number. (I also had issues at the time there were multiple forks of this software, merging each other, causing conflicts on the version number.)
At GitLab we just bump the minor version every month. And when there is a breaking change we bump the major version. The advantage is that semver is well understood and we can communicate breaking changes. The disadvantage is that you need to look up what version was released when.
For a mature program (not a library) I like using a combination year.counter. No semantic meaning at all apart from monotonous improvement, and you can tell how old a version is.
44 comments
[ 3.4 ms ] story [ 90.4 ms ] threadIf it spreads the idea that there are several ways to do versioning, and not only semver, and it gives a proper rationale, I'm ok with that. (server.org might be difficult to register though).
CalVer is just a pattern with a name so you can talk around about why Ubuntu uses year.month as version, and some ideas on when it is good to use it or not.
* Do users declare a dependency on "= 37", or ">= 37"? (Or do they have the option of either?)
* How do you push backwards-compatible changes like bugfixes? Is the rule that every new release is a new version number?
* If you do the >= thing, do you automatically take the newest compatible dependency or the oldest possible one (vgo-style)? Do you run tests on reverse dependencies when dependencies are updated?
* Are you allowed to have diamond dependencies, e.g., A depends on B and C, B depends on AdminTools 37, and C depends on AdminTools 38? Do you resolve that with a single copy of AdminTools, or two?
"= 37".
> How do you push backwards-compatible changes like bugfixes? Is the rule that every new release is a new version number?
A backwards-compatible change gets a new version number, like any other change.
The system does actually let you use version numbers like "37.1", but it doesn't assign any meaning to them. They're all opaque symbols, really. You could just as well have versions "first", "new", "improved new", "new new", "new new (final)", "new 2", etc.
> If you do the >= thing, do you automatically take the newest compatible dependency or the oldest possible one (vgo-style)?
We don't use ">= 37".
> Do you run tests on reverse dependencies when dependencies are updated?
Oh god no.
Personally, when i'm upgrading a library, it will usually be to address a need of a particular consumer, and i will develop the upgrade and the change to the consumer in concert. So that's sort of testing it with a reverse dependency, but only one.
> Are you allowed to have diamond dependencies, e.g., A depends on B and C, B depends on AdminTools 37, and C depends on AdminTools 38?
You can express a dependency like that, and if there is a conflict, the package manager shouts and you and stops. You then have to write something like "no really, use version 38" to persuade it to work.
> Do you resolve that with a single copy of AdminTools, or two?
A single copy.
To my mind, these are all questions about our package management tool, not our versioning scheme.
Looking at the example CalVer projects: Ubuntu follows this model because it's the model of basically all UNIX distributions, that a particular command name doesn't change meaning / API without either a deprecation cycle or an explicit action from the sysadmin. If a command changes incompatibly (e.g., Python 2 to Python 3), it gets a different name (e.g., /usr/bin/python to /usr/bin/python3). If a library changes incompatibly, it either uses symbol versioning infrastructure to provide old and new versions of the function (e.g., fmemopen@GLIBC_2.2.5 is now provided by libio/oldfmemopen.c, and the fmemopen@GLIBC_2.22 implementation, which fixes tons of bugs and drops questionable features, is only visible to programs compiled on a system with glibc 2.22 or newer), or the library itself has a new filename (e.g., at some point libcurl.so.3 became libcurl.so.4). Therefore, code that compiled and ran fine on, say, Ubuntu 16.04 should generally be expected to compile and run fine on Ubuntu 18.04, or less commonly, not run at all with a clear error up front about the specific missing functionality.
Twisted, youtube-dl, and pytz follow a similar model. Code written against them years ago should generally continue to work; the version number provides information on new features, not on deprecations.
Teradata UDA is different, but it follows the underlying CalVer philosophy of breaking changes being something to avoid. (But it uses a year and month to indicate an API compatibility level, which isn't how the rest of the CalVer projects use dates.)
So the answers to my bullet points for CalVer are generally ">=", "Rename the functionality, do not ever redefine an existing name", "Newest compatible dependency is fine and you should definitely have integration tests," and "Diamond dependencies to different versions aren't a problem, diamond dependencies to different names e.g. libcurl.so.3 and libcurl.so.4 are discouraged but should work".
Take for instance Ubuntu - should those releases be considered unofficial? [no]
How about the python-attrs library - should those be considered interim? [no]
All versioning schemes fail somewhere and CalVer and SemVer (and certainly others) are not exceptions to this rule. The problem is you’re reducing a ton of metadata into a human-readable, compact label - and that’s inherently lossy.
IEEE standard.
http://pubs.opengroup.org/onlinepubs/009695399/functions/str...
Also, for a more technical audience, the C, C++, and EcmaScript specifications.
3.11, 95, 98, ME, 2000, XP, Vista, 7, 8, 10
I wouldn't call that a pattern.
https://www.gaijin.at/en/lstwinver.php
Windows Server is what I was thinking of.
Windows
1, 2, 3, 95, 98, ME
Windows NT
3, 4, 2000, XP, Vista, 7, 8, 10
Windows NT Server
3, 4, 2000, 2003, 2008, 2012, 2016
Also, I think version strings should contain information about compatibility, not just about time.
I find semver useful for libraries but mostly meaningless for projects that have no dependents like a web server or a website.
Not much project would benefit from this kind of versioning. Only websites, desktop apps with no config files, etc.
https://pypi.org/project/pytz/
Latest version: 2018.4
This makes a lot of sense for a library like pytz, which provides an interface to constantly-changing data; you don't want the version to indicate APIs, you want it to indicate the time of last data update.
CalVer is not a format, it's only about using dates as versions. Each of the project on the page has a different format.
I’m interested in your war stories, pm me.
The key here was that the rapid pace of the project kept breaking various user segments' expectations about semantic versioning.
It seems very odd, to me, to make this a variable-length field. If you're planning out to 2106, why would you not use a full length year? This particular bike shed should clearly be blue.
It's more for the past (06) than for the future.
No reason other than that.
I also change the background image of the login page on 'major' releases where significant new features are added as a subtle visual cue that users should go check out the change log.
It's often the little touches that have a disproportionate impact on user engagement.
What is the trailing 1? Are you doing multiple releases per day? Is an emergency bug fix release on June 8th 2018-06-08.1 or 2018-06-06.2?
It's rare but it happens.
I chose to start at .1 and not .0 because users don't really get 0-based releases (at least mine don't).
I wrote a small piece of code in my project's setup.py [1] that computes the version number from the timestamp of the last git commit. It's very useful because it removes the need to make a commit for the sole purpose of updating the version number. (I also had issues at the time there were multiple forks of this software, merging each other, causing conflicts on the version number.)
[1]: https://github.com/ProgVal/Limnoria/blob/testing/setup.py#L5...
2017.75, 2018.76, 2018.77, 2019.78 etc