77 comments

[ 2.7 ms ] story [ 131 ms ] thread
For those trying to compare it to SemVer, ChronVer seems to have a different target. It mentions web applications and systems that see releases (with continuous delivery or not).

SemVer remains good for libraries, or anything where there's a clearly-defined API for consumption, because it clearly establishes a set of rules for what to expect. If you're using version 1.0.0 and upgrade to 1.10.30, with SemVer it would remain perfectly compatible, and no change should be required on your part (that's the ideal; of course developers are free to use whatever versions they want and pretend it's valid SemVer).

For applications though, I agree that ChronVer seems far more useful, because there's just no real "compatibility" contract with an application. After all, macOS 2017.09.25 seems far more descriptive than "10.13", which means nothing to anyone.

> For applications though, I agree that ChronVer seems far more useful, because there's just no real "compatibility" contract with an application.

It's unfortunately becoming less common but there is still a "compatability contract" between developers and users. With 10.13 -> 10.14 upgrade I would expect no major disruptions as a user, I would expect the UI to be nearly identical and no features to be dropped. With a 10.13 -> 11.1 upgrade I would expect "breaking" changes to my interactions and plan to install the update at a convenient time to deal with this. For a 2017.05 -> 2019.08 upgrade I have no indication of what to expect.

For something like continuously delivered web applications I don't think any versioning scheme makes sense to user because they have zero control, it's useless information.

This is how we do it at work. Features and major changes go into trunk, which gets released in the new versions. Bug fixes get released in earlier versions and up.

We do major.minor version numbers, with even major versions being releases and odd being development versions. We also have an user-selectable way to pick version (major and minor) of our program at launch. So if we "dun goofed" in one version, the user can always just relaunch the previous version which worked.

Right before xmas I changed it so that if you didn't enter a first name, it would skip the rest of the name and address fields. This went into a new major version, so users were not that surprised to discover this rather big change in UI flow.

However I had missed that in one special case, users were required to enter a postal code. The user that reported this could then just launch the previous major version until I got that issue sorted, getting bug fixes for other issues in the meantime.

> For applications though

Often have thought that crypto libraries should be using ChronVer. Unlikely that an implementation will have breaking changes.

Besides that certainly agree with everything else.

“ After all, macOS 2017.09.25 seems far more descriptive than "10.13", which means nothing to anyone.”

I don’t understand this argument. Dates are arbitrary, and are subject to how quickly something is released/iterated on. Was there 2 releases a year? Once every two years? It’s hard to understand between different versions how many major potential changes there are.

For something like macOS, the number of fundamental under the hood changes are tremendous, coming from a super large organization. Without semantic versioning, it is difficult to create dependencies or explain compatibility, let alone understand how “old” a version someone might be on —- time itself is subjective, while an incrementing counter is not.

incrementing a counter is as informative as taking the current date. if you want to embed some information about the software in its version number, you have to define the protocol either way, i.e. what events increment a counter.

see also vector clocks.

You should write a spec for vectored versioning and register vecver.org to host it :)
bzr may have done it already, disregarding the fact that build version != source version.
> After all, macOS 2017.09.25 seems far more descriptive than "10.13", which means nothing to anyone.

And then 2017.09.28.83456, 2017.09.28.83457, and 2017.09.28.83458 all come out at the same time.

One is a patch to El Capitan, one is a patch to Sierra, and one is a patch to High Sierra.

There's absolutely no way to tell which is which.

Go ahead and refer to Catalina as 2019.10 or 19.10 or 1910. But keep the same prefix when patching it.

Wouldn't it be something like this?

- 2017.09.28-capitan - 2017.09.28-sierra - 2017.09.28-highsierra

These are perfectly valid in the spec.

There’s one or two Perl5 modules that are version yyyymmdd.nn that expect you’ll only do less than 100 releases per day.

It turns out one of the more interesting problems with version numbers is tools that assume that each number will fit within 2^8, or 2^16.

The idea rubs me the right way but why not 1, 2, 3...

I think a more modular versioning standard might be good as semantic versioning can be very helpful in certain projects like libraries.

Like if your project has x abstract property then include x. and y for a x.y versioning scheme.

To paraphrase Rich Hickey: "Just use a different name."

Compatibility of APIs is retained if you require less or provide more. Breaking changes happen when you require more or provide less. Don't do it. Add a new and different function that requires more or provides less. Keep the old one. You now have a backward compatible interface.

Seems to be solving the same problem as CalVer[1]?

I've used it in the past for Continuously Deployed web applications. The dates make it easy to determine the age of the release, and it's easy to automate away to a CI job[2].

[1] https://calver.org/ [2] https://dephell.readthedocs.io/cmd-project-bump.html

The author of CalVer emailed today, I had no idea their project (or mine) was posted to HN! But yes, they are similar.

I'm biased but I like mine more (and have an npm module to increment automatically).

I like versioning things as YYYY.MMDD.HHmm

2020.0102.0345 if you stick to GMT.

Stylistic versioning was conquered years ago by TeX.

"The current version of TeX is 3.14159265; it was last updated on January 12, 2014. The design was frozen after version 3.0, and no new feature or fundamental change will be added, so all newer versions will contain only bug fixes. Even though Donald Knuth himself has suggested a few areas in which TeX could have been improved, he indicated that he firmly believes that having an unchanged system that will produce the same output now and in the future is more important than introducing new features. For this reason, he has stated that the "absolutely final change (to be made after my death)" will be to change the version number to π, at which point all remaining bugs will become features. Likewise, versions of Metafont after 2.0 asymptotically approach e (currently at 2.7182818), and a similar change will be applied after Knuth's death."

https://en.m.wikipedia.org/wiki/TeX

> he indicated that he firmly believes that having an unchanged system that will produce the same output now and in the future is more important than introducing new features

I sincerely wished that that was a more common sentiment. All this change for nothing doesn't move the needle at all and irritates users the world over.

(comment deleted)
Oh really? You want an IE6 situation to happen for every software out there?
I have enormous respect for Knuth and his versioning joke is cute, but effectively it just means development and feature additions happen in forks or new projects, such as LaTeX and the many others.
Or maybe there is wisdom in thinking of applications more along the lines of protocol stacks.

A TeX layer implements mechanism, a LaTeX layer does the policy, and an Overleaf handles the UI chores.

(I'm a Spacemacs/org-mode/texlive user, personally.)

In some ways that isn’t a bad idea. I was looking at the plans for Express the other day and it struck me they would be better freezing features and encouraging people who wanted async to look at something like Koa. As it is they will probably end up with the worst of both worlds.
Two big problems with this:

- Does not support multiple release tracks. What if you have 'version 1' and 'version 2' with both receiving updates?

- Cannot visually guarantee that any version is greater than, less than, or equal to another. If you're using git, mercurial, or any other VCS with hash-based commit IDs, how can a user tell which release for a particular day is the latest?

This is probably fine for SaaS stuff where you only support one version at a time and the version number is more important to you than your users.

When version 1 receives an update, that's how we got version 2. The update to version 2 is version 3.

I don't see what is here to misunderstand.

If someone wants to take just a subset of the changes between 2 and 3 (say just one fix) and backport it over version 1, they are on their own.

That's a downstream problem that the project doesn't have to cater to in its versioning scheme. The project issued a bugfix, and that bugfix was applied when version 2 was superseded with 3; end of story.

(comment deleted)
The problem was not with a random someone wanting to make a change to version 1, it was with the project wanting to make a change to version 1. That is not a downstream problem.
The problem with this is that it is not practical. One example I can think of on the spot is an old tool written to support Windows 7, a bridge version supporting win 7-10 and a new version supporting only 10. Most industries do not move at the same pace (We have some projects that can only run on Windows 95). So some security updates to the bridge version adn the new version are made simultaneosly and corresponding update to their version numbers.

Another example is python 2 and python 3, where python 2 was getting updates form 5+ years after python 3 released.

Python 2 and Python 3 are different languages that get installed side by side.

Two completely independently developed C compilers, or independent implementations of Common Lisp are much more compatible with each other than are Python 2 and Python 3.

They could as well have different names (think Perl vs. Rakudo).

Racket is more compatible with Scheme than Python 3 is with Python 2, yet has a different name, for good reasons.

Both Python 2 and Python 3 could respectively use a simple versioning numbering scheme. Python 3 version 1, version 2, version 3, ...

The example I gave on the site with "UI and implementation" branches could work. I haven't done this in practice so I cannot confirm.

Idk how you'd be able to tell anything about a commit from its hash tbh.

You're right though, I primarily create/work on SaaS products and this versioning is perfect for it.

I think my original reply was a little brusque. Really this is a fine scheme, but I do think it's limited in where it can realistically be used - IMO, small self-contained SaaS products only - and I do think that the hash should be called out explicitly as metadata vs. an integral part of the version.

Simply moving from YEAR.MONTH.DAY.CHANGE_ID to YEAR.MONTH.DAY.UPDATE.CHANGE_ID, with UPDATE being a mandatory monotonically-increasing integer, would make this scheme more attractive to a wider variety of users.

> If backwards incompatible changes are introduced you MUST append a hyphen and the reserved label "break". For instance: 2006.04.03.12 > 2006.04.03.12-break.

So with this system if you want to know whether two versions are compatible you would need to check versions of all releases between them for not having that label...

It's the best I could come up with when drafting this. What would you like to see?
Too complicated. Here is a better chronological versioning: 1, 2, 3, 4, ...

Given two versions j and k, those being integers if j > k, then j is chronologically later than k.

That's what I use on the TXR project.

That being at version 230 means I made 230 releases since the project started in 2009.

Since that number "means" something, it has semantics; therefore, it is not only chronological but also semantic.

Totally agreed. This is seriously overthought. Users need to be able to tell in an eyeblink what the order is without reference to some standards document. Presumably everybody to whom it applies can count.
For a web app under continuous release, I would suggest doing both.

230-12-01-2020 to mean the 230th release since the start of the project, made on January 1st, 2020.

Why not 230-2020-12-01 to avoid the awkward 'US order or 'most-of-the-rest-of-the-world order' issue?

Edit: the great irony is that your example included an error which I then reproduced! Obviously, 1st jan is unambiguously:

230-2020-01-01

If you were representing 1st dec, though, I’d suggest:

230-2020-12-01

Actually I was trying to avoid the US date standard myself, then got confused and wrote a date that follows no standard that I know of. My mistake entirely.
230-12-01-2020 to mean the 230th release since the start of the project, made on January 1st, 2020.

Surely this is either released on the first of December or the 12th of January?

While versioning for libraries is important, and one could argue about different ways, app versions (ie mobile or desktop) with anything other than whole integers is dumb. Our mobile apps have versions like X.Y.Z and X changes at random times for no reason, and in general in mobile you never see the version anyway, and with auto updating on customers don't even notice.
This will break when you need to support two tracks of your library. Also, deprecation is not a breaking change; it is notification that a breaking change is in the pipeline, but hasn't happened yet.

A better approach would be hybrid:

2.2020.01.01.3-bd

Where the first number is the release track, the next three are the date, the last number is to handle multiple releases in a day, and the letters (always in alphabetical order) are codes for:

b: this is a breaking change

d: there are deprecations in this release

I would also suggest a third letter code "z" to mark bad releases, where you unintentionally broke things to the point that this release should not be used (and for whatever reason cannot retract the release). So, to mark the above release as bad, tag the exact same version a second time:

2.2020.01.01.3-z

Which means that 2.2020.01.01.3 is a bad release. It will show up after 2.2020.01.01.3-bd because it's later in the alphabet in the release list. Anyone looking for the last release that matches whatever their criteria is will now know to avoid that particular version.

Edit: Formalized here: https://github.com/kstenerud/hyver

That's not a bad idea at all to include some sort of semantic tagging to the version, the suffix is a great place for it too like a footnote of sorts.
Has the same fundamental problem as tfa: codes are not sticky / spanned so to know what sort of changes you should expect between two versions you have to look at every intermediate.

Meaning the tag is useless. If a release is “bad” you just yank it.

> it will show up as after 2.2020.1.1.3-bd

According to your scheme the last number is the release index in the day so this is nonsense: either -z replaces -bd or it’s a later release which appears before in the release list (assuming you have a descending alphanumeric sort).

You wouldn’t have both 2.2020.1.1.3-bd and 2.2020.1.1.3-z because they’re the same release with different crap tacked on.

> codes are not sticky / spanned so to know what sort of changes you should expect between two versions you have to look at every intermediate.

The only changes people usually care about are "will it break my software?" and "Am I using deprecated calls?" Beyond that, there's no standardized way to know what has changed between releases, and you'd have to look at every release regardless. Of course we could always add another metadata letter "n" to signify "new features". In fact, I'll add it...

> If a release is “bad” you just yank it.

And what if you can't yank it? That's what the "z" tag is for. If you can yank it then by all means yank it.

How are you going to relabel it when you can't yank it?
You don't. You create a second "marker" release whose only purpose is to announce to the world that this release is bad.

Example:

Current release list:

    ...
    2.2019.12.18.1
    2.2019.12.19.1-n
    2.2019.12.19.2
    2.2020.01.01.3-bd
Bad release you can't yank: 2.2020.01.01.3-bd

Create a new marker release: 2.2020.01.01.3-z

New release list:

    ...
    2.2019.12.18.1
    2.2019.12.19.1-n
    2.2019.12.19.2
    2.2020.01.01.3-bd
    2.2020.01.01.3-z
Now everyone knows that 2.2020.01.01.3 is bad but couldn't be yanked.
> The only changes people usually care about are "will it break my software?" and "Am I using deprecated calls?"

Which is exactly what your scheme does not answer unless you go through every single version between current and latest one by one, at which point… what’s the point if your scheme? You’re going through every single version anyway you can explicitely list the changes.

> And what if you can't yank it?

Then you add support for yanking.

Or just start at 1 and increment every build.
Changeset isn't enough. You need build number too or rebuilds will collide.
A major version number is still needed. ChronVer seems fine as a replacement for minor and micro versions (and rc and other designations).
I just wish the idea of software being finished came back into vogue.
The Motivation is dependency hell, the Rationale says the version is easy to understand at a glance. So, Rationale has missed the opportunity to explain how this is a solution to the declared problem.
Exactly. I don't get it. What's the advantage here?

If the complaint is "semver numbers aren't always used the way they are meant to be" how does (what is effectively) `v<year>.<month>.<day>` solve that problem? With SemVer the numbers are at least _sometimes_ used as designed.

If the complaint is "I want to know how old the software is" then why not just append a timestamp to the SemVer number as in `v2.3.4-<date>` What's the advantage of making the date primary?

Does the breaking-change identifier only refer to the previous version chronologically? Then this system is incompatiable with any scenario in which you need to "patch" old versions of the software. And isn't that a pretty central problem that version numbers are meant to address?

I honestly don't understand the problem chronological versioning is solving, or if I do than it seems to me that `<semver>-<date>` is obviously a better way to solve it.

Honestly, the intent there seems easy: it's hard to tell at a glance that library A version 230 is compatible with library B version 1.2.4

However, if they both used date-based versioning, then the compatible versions would in theory have the same-ish version of 2019.12.* when compatible with each other.

For the same reasons as in tons of other comments, I also don't think this theory would work out.

(comment deleted)
We used this version scheme at my last job. I have fond memories of it. It’s easy to think in days/months/years. When a bug was found we could load old versions to see when it first materialized. Because The date information was contained in the version it was easy to remember what else was happening around the same time and what could have caused it.
Yes, that's one of the reasons I created this. Well, bought a domain name and made a webpage anyway, haha.
There's a RFC 1912 from 1996 explaining this versioning strategy which was already in common use for DNS SOA record serial numbers.

https://tools.ietf.org/html/rfc1912

I'm sure this strategy vastly predates 1996 in other computer applications.

Julian dates have a similar purpose, and have been used in the US Military Logistics system for a very long time, but I can't figure out how far back. Also some food products, beer especially, with "secret" expiration or manufacture dates, according to civilians anyway, are often strangely formatted Julian dates.

Agreed it's not a new idea, but I'm not discounting adding a catchy name to it and a well-made website with a clear specification. That will get people using it in some open-source communities (Node / JavaScript specifically).

The problem with SemVer is that library owners are not strict with its intended usage (backwards incompatible changes in patch!) or don't fully understand it. Perhaps having a much more simple versioning strategy will help in some use cases.

> The problem with SemVer is that library owners are not strict with its intended usage

The worst case I saw was when someone was just appending 9's to the version number every release (0.99999 -> 0.999999). I think this was a joke about asymptotically approaching version 1.0. The effect was that some error message when solving dependencies came out as "X wants version 0.99999999999, but Y wants version 0.99999999999". Very annoying!

> The problem with SemVer is that library owners are not strict with its intended usage

What's the point of doing it if you're not gonna do it right?

Thanks for the kind words!

Sorry for the super late followup, but yeah my point was a pretty good selling point might be its been an operationally useful, apparently optimized, and successful solution for many decades in the fields of X, Y, and Z, and the more examples of success, the better.
I was eight years old in '96. I wish this versioning strategy caught on then.

Maybe I should add a "prior art" section on the site, thanks for the link!

Tesla does something similar. They use year.week.patch.build (or something like that).
Version number is too blunt an instrument

Ideally I want enough introspection in my tooling to automatically pick up breaking changes by doing static analysis between versions and where breaking changes are impossible to detect I want humans to tell me what the breaking changes are in a structured way

I want my dependency manager to take me up to the next breakage free version and to see what the breaking changes are if specified, maybe with programmatic suggestions/fixes from the library maintainer

And perhaps hardest of all I want the culture of change to being hyper allergic to breaking changes in the rich hickey sense

begins having to have version to manage dependencies this does nothing to solve dependency hell, to the point I question whether the author has a clear understanding of the problem space
Pollen has my favorite version numbering system, a blend of semantic and chronologic:

> “Consistent with Racket’s version-numbering system, the first digit reflects major updates to Pollen that break backward compatibility. The second digit reflects feature updates that don’t affect existing features. > > “Inconsistent with this system, Pollen’s version also appends a build number, which is the age of the software in days and minutes.”

https://docs.racket-lang.org/pollen/version-notes.html

As far as I can tell I actually tend to use something like this also (semver annotated with some form of timestamp, if only so I can tell at a glance how old something is, but usually as two distinct fields), but even after looking at the linked site I'm confused by what "the age of the software in days and minutes" means exactly.

The current version is reported as 2.1.2329.415.

Does that mean this release (roughly v2.1.something) was compiled/packaged/released 2329 days and 415 minutes after some arbitrary project-start timestamp? That this was compiled/packaged/released 2329 days and 415 minutes _ago_? Are those literally the "days" and "minutes" fields they're talking about?

The former seems kinda crazy (as the version number would be mutable) but to be honest that's how I'd naively interpret "the age of the software" - I want to know how long ago this build was created, not how long after the project was first started.

If the version number is days-after-project start, why not just use the actual date? What's the value of the arbitrary baseline?

(Actually, now that I think about it I guess the arbitrary baseline does have an advantage if you are intentionally trying to obscure how old the build is in calendar time. *.2329.415 provides an easily interpreted, monotonically increasing identifier without making it obvious if this release happened yesterday or 10 years ago. I.e., this solves the problem that products with the year-of-release in their name have - surely people are eventually stop buying FooBar2015, whether or not there's a real need for an update in the past 5 years.)

Okay, but I _rarely_ need to know _when_ a version was released. I almost _always_ need to know how a version relates to the version I'm currently using. SemVer does this, and to an extent, "normal" non-SemVer also does this. ChronVer is a textual representation of that green calendar on GitHub.
Ok for user facing programs horrible for libraries as you can no longer tell which versions have just internal bug fixes, which add additional interface in a backwards compatible way and which are being changes.

Also this makes it impossible/stupidly hard to release security patches for a older but stable version of a library when a new version with a largely changed API is released (which might not yet be feature completely)

Sure there are some ecosystems what a lot of authors don't do semver right, or where due to (mostly dynamic) aspects of the language this is harder then it should be.

But why not: 1. Require semver for a ecosystem/package manage which clear rules as what constitutes as a breaking change 2. Remove/ban packages which don't keep with it (or at last put a warning up). Authors who don't like it can still release a major version update Everytime.

Honestly if this schema would be used in a library ecosystem thinks would be pretty fast become pretty bad with you having to pull in tens of versions of the same fundamental dependency which might even all be compatible but you can't tell automatically and have to check by hand which isn't something you can afford time wise.

I don't much care how you choose to number your versions, as long as you publish release notes with it that say what has changed since the previous version. If I have to start poking around in the commit history of your GitHub project and trying to suss out how those commits correspond to published releases, I start to get a little irate.

This is an enormous pain in the ass with .NET NuGet packages, I have found, although I am sure it extends into other ecosystems as well.