Say No to ZeroVer: Start with 1.0.0

5 min read Original article ↗

Disclaimer! This is a rant that contains my strong opinions. So remember: I'm a nobody, and you don't have to listen to me. Open source maintainers have enough on their plate already, and owe nothing to people who aren't paying them any money.

Additionally, this post is about SemVer as defined by its specification. If you're not doing SemVer, that's just fine, and this doesn't apply to you.


That said, we really need to stop publishing SemVer packages at 0.y.z.

The entire idea of SemVer is that downstream users can have some semblance of safety when updating dependencies. Any backwards incompatible changes require a major version bump, immediately alerting downstream users that something needs manual attention. No one can update to a breaking version accidentally. A proper dependency management system usually gives understandable instructions on where the issue is if your dependencies require a version that's newer than you can support.

ZeroVer — the practice of using a major version of zero — throws the guarantee away. Some people believe it to mean that breaking changes are done by bumping the minor version, but the SemVer specification is quite clear on this:

Major version zero (0.y.z) is for initial development. Anything MAY change at any time. The public API SHOULD NOT be considered stable.

In short, there's no guarantees about anything. No version range you define can be considered safe — unless it is a range with exactly one version — and no version update can be trusted. Even if you make sure not to use ZeroVer dependencies yourself, they probably exist somewhere in your dependency tree and are waiting to bite you in the behind. This is exacerbated in languages where you can only have one version of a package at a time, such as Erlang, Elixir, and Gleam. Gleam's zero-versioned standard library routinely breaks projects, when one sub-sub-sub-dependency requires a newer version which has removed a function used by other dependencies.

In dynamically typed languages, these issues might hide until runtime, poisoning some rarely run code path that has been missed by your test set. The package manager offers no help as in it's point of view everything is just fine. Even if the failure occurs at compile time, it can be difficult to weed through the dependency tree trying to find out which exact version is broken and who in the hell caused the version to update.

Another small thing that ZeroVer breaks is safely staying on older versions. If a package has bumped its major version and there are no security issues mandating an update, staying on the same version is just a matter of doing nothing and the package manager will make sure all your dependencies work with this restriction. If the package is zero-versioned, you've likely already set a version range that is too loose — namely any range containing more than one version — and need to adjust it to add the restriction after you've noticed the breakage.

While you might think that a major version bump requires more effort as it needs manual intervention, it's the only safe way we have and spares us from wasting a lot more energy debugging issues after the fact. To finish my rant, I've compiled a small list of usual questions and answers to them.

My package isn't ready yet

Is anything ever ready? Furthermore, it doesn't need to be! SemVer does not require or suggest anything special about 1.0.0 other than:

Version 1.0.0 defines the public API.

When you publish a package to a package manager, you are already publishing a public API! Whether you want it or not, people will start relying on it and building on top of it.

If you're really, really unsure about your package's API, then don't publish it. Put it in a public git repo and post the link; most package managers have a way of installing git dependencies. If someone is so desperate to use your package that they'll get it straight from git, I'd personally feel that they can also accept full responsibility of making sure updating it won't break anything.

But then I'll quickly bump the major many times

We aren't going to run out of numbers! The notion of bumping the major version meaning "something big" is something we should rid ourselves of, if we are to take SemVer as it's specced. Additionally, the friction of bumping the major version should also give you pause; oftentimes old behaviour can be maintained, even if as a deprecated version, and you can batch the removal of several things into one bump.

I can't find it now, but I'm pretty sure I'm not the only one to think of a new concept to alleviate this pain: era versioning or EraVer. Simply put, slap another number in front and call it the era. Bump that when you've made something really big and fancy. The rest of the version can follow SemVer, except that major version 0 would actually be counted as a regular version. Alas, this system is not in use anywhere that I know of, but it's a fun thought exercise.

Updating to a new major version is too complicated

As a counterargument, updating to a new zero-based version is only simple on the surface. The devil is in the details, and from that point on it's just a matter of how Fortūna is favouring you that day. A bug in production is much more complicated to handle than an API change in the development stage.

You can't trust SemVer anyway

People make mistakes, sure. It's still the best we have, and personally I don't want to let perfect be the enemy of good.

I do what I want

That's fine and I can't demand anything else of you, as I don't pay your bills.