Please see the disclaimer.

Assumed Audience: Hackers and anyone interested in Yzena’s software.

Epistemic Status: Confident because it’s my choice anyway.

Introduction

Yzena is my business. It’s a software business, obviously.

In July 2022, I wrote about how Yzena will version software.

I have made a few tweaks since then, so I am writing an update.

I do repeat information in this post so that it stands alone and remains a reference document for Yzena versioning.

Versioning Systems

Anyway, what versioning system should Yzena use?

SemVer (Semantic Versioning) is okay, but not great. It is standard, though.

But there are other ways.

EffVer

One is EffVer.

I like one thing about EffVer: it encourages updating the version number when the impact is bigger than intended.

But beyond that…it isn’t much. Judging “impact” is still a human process, and like everything human, it will always have problems.

I wish there was some way to alleviate this, and kudos to EffVer for trying, but I don’t think EffVer will work for Yzena.

CalVer

Another scheme CalVer (Calendar Versioning). This one is both new and familiar to me.

It’s new because I hadn’t heard of it until recently. It’s familiar because it’s what Ubuntu uses, and Ubuntu was my first Linux distro.

CalVer is interesting because it says nothing about how to version software except to include a date somewhere.

This gives me a lot of flexibility.

In addition, CalVer has two questions to ask:

  • Does your project feature a large or constantly-changing scope?
  • Is your project time-sensitive in any way? Do other external changes drive new project releases?

The answer to the first is YES since it’s a monorepo.

The answer to the second will be YES, even if it’s not obvious.

One of the examples they gave is:

Business requirements, such as Ubuntu’s focus on support schedules.

Since this is for a software business, I sincerely hope that Yzena software has to care about support schedules.

So CalVer makes sense for Yzena software.

So I just need to choose a scheme and be done, right?

Not so fast.

Editions

One of the CalVer examples they gave was of Teradata. Teradata has an interesting scheme that I think the CalVer website describes well:

Teradata’s usage is notable not for the prominence of the technology or company, but because there have been multiple releases in 2016 which were versioned as 15.10. This may seem breaking at first, but the meaning and utility is clear.

The library maintainers have crafted a resourceful hybrid of semantic versioning and calendar versioning. The YY.MM part of the version are used as a combined SemVer major version. That is, for new releases, the API of the library remains the same as it did in October 2015. Dependent code written since then is safe to upgrade. We will see the year and month segments update next time there is a breaking API change.

I like this, for several reasons.

One, it’s kind of like Ubuntu’s version scheme, and since I care about support schedules, this seems nice to me.

Two, it’s also kind of like Rust’s edition scheme. I have a programming language myself, and Rust’s edition scheme seems to be the best for that.

Three, I can make the rest of the version anything I want.

But there is another thing I want from the version: a marker of how many versions there have been in an “edition.”

Scheme

So, without further ado, here is Yzena’s versioning scheme:

E0Y.E0M.0INC.0Y-0M-0D

where E0Y is the zero-padded year for the edition, E0M is the zero-padded month for the edition, and 0INC is the zero-padded, increment version number.

Let’s go over it all.

Edition Components

First, there needs to be two parts for the edition because, unlike Rust, I want the month.

Why? To have finer granularity. And because I expect to release new editions more than once a year. At least at first.

Version Increment Component

The third component is the number of releases in the edition, starting from 0.

This means it always increments or drops to 0. It has to change on every release.

It also tells the user something important: how many times has this edition had a release?

Yes, this is important. Say I release a 23.07 edition, and it is still the active edition 10 years later. If the increment component is 01, then people might rightly question whether the project is alive, but if it is 57, then people will probably understand that the project is alive. And not only that it’s alive, but that it’s stable.

That component is important because it will show how well-off the project is when combined with the edition.

And it is two digits so that older versions sort before newer ones.

And I don’t think I will have 100 releases in one edition. If I do, I may as well make a new edition anyway.

In addition, the truth is that there was a second reason it always increments or resets: so that the version is always unique.

Why Three?

So…what it sounds like is that there are really two components: edition and increment. Why do I still separate the edition with a period?

Well, I do it because Ubuntu does it, and personally, I think it looks better than a four- or six-digit number.

Interfaces

There seems to be people screeching! Let’s see, who did I forget…

Ah! The SemVer and EffVer people! You want versions to be a contract, right?

I disagree.

Hey, what’s with the torches?! I’m not done yet!

You see, I have used SemVer for another project, but judging impact turned out to not matter.

Why? The project is two programs and a library. Everyone uses the programs, and no one uses the library.

I need SemVer for the library, but not the programs.

Eek…

There was one time I made a breaking change in the library and didn’t bump the major version. It was a calculated risk.

And you know what? It paid off.

Why? Because no one uses the library!

I wish I hadn’t used SemVer for that project; it has proven useless.

Put down the tar and feathers!

Because I understand that you want some contract, and I have one for you: I will version interfaces, not implementations.

My monorepo will have multiple versions. Each one will be tied to a specific API or module.

Multiple API versions will be supported for everything, and you will be able to select the API versions at build time.

In addition, any deprecated API that is removed will turn into a hard build error.

You can use this to upgrade your use of my code at your convenience with the guarantee that your code will only break if you act molasses and don’t get off your derrieres.

And also that it will break, loudly.

Oh, and I’ll update the edition whenever an interface is removed, so it will be obvious when work needs to be done. And I’ll carefully document the changes.

See, I’m not cruel!

Conclusion

So that’s the scheme, and that’s why. I hope it works for you all, whether users, packagers, or code archeologists.


Edit, 2024-04-01: I removed the version data component. It was just too unwieldy.

To get the date of a version, users will have to refer to the versions.gaml file in the Yc repo.