Post

Big fan 🙂 I've been using the other mentioned RomVer for a while, but too many people wrongly assume it's SemVer as it looks the same

Why not 4 numbers? Using this 99 majors per epoch seems to be begging for issues. Feels like storing years with 2 digits. The assumption of why would anyone need more than X of anything seems to always cause problems.

I used stardates for some Android apps. My idea of “stardates” is the number of days since 00:00 01-Jan-1970 UTC, to one decimal place. E.g. the stardate right now is 20096.2. What!? I missed 20000.0, just about a minute and a half ago!?

While I agree about this issue with SemVer (and I unfortunately follow the same bad 0.x practice as you, instinctively), I don't feel well thinking about 100-based version numbers. v100.x, v200.x etc feel overwhelming

I don’t like it either, it doesn’t look elegant. But I have to accept it’s logical better than 0.x, under the requirements of being compatible with SemVer.

The big jumps in major versions would take some getting used to, but ultimately, it's intent is to convey as much relevant information as possible. I also have a problem moving out of version 0 with my code for similar reasons, but I could see the grand leaps in major being somewhat confusing

It could work if major versions did not end up as part of the product name. It's easier to search for Nuxt 3 docs than for Nuxt 300 which is in the same place as 301, 302... That's why i like when groundbreaking changes are published as a new package. Nuxt3 1.0.0

Yeah it’s a common misconception! Versioning is for humans *and* computers, and semver has a fair number of concessions for both sides to get their work done. Us poor humans need to understand the spec the same as the computers do, which takes reading it. Humans prefer to complain about it instead 🤣

That's a neat hack on top of SemVer. As a heavy user of semantic-release, I'm wondering how we might denote the jump from 100.x.x to 200.x.x. Semantic commits make it easier to manage smaller version jumps, but incrementally shipping big rewrites like this might be more challenging.

Not using a zero major version for "ready" software is the important part. :) Otherwise, Epoch is fine but the truth is that SemVer has enough judgement calls in it that devs need to understand how major and minor updates are interpreted *per project*. (Hopefully, patches are truly only bug fixes).

I can tell you several horror stories of teams playing games with their version numbers and painting themselves into very painful corners. It is amazing how "clever" some devs can be.

> major and minor updates are interpreted *per project* 💯 Totally! Also, when using open-source projects, users are technically required to review every single change, as there are no warranties. In reality, people tend to trust maintainers as well as versioning, which is why it's still important.

I’d love to have 4 numbers versioning. But as I mentioned in the post, it doesn’t work with npm’s ecosystem as it assumes SemVer every where. This is my workaround to that :P

I lean the other way. I think that we should have breaking changes and non-breaking changes. v12.456 for example. Note that this can be parsed into a real number which is a lot friendlier for things like Prometheus… plus the majority of devs I work with don’t understand semver anyway.

I like the compatibility approach. One thing I don't like is until you finish one epoch there's no way of telling you're using this scheme, so I'd say ESV should start at 101.0.0

Interesting thoughts Anthony! I like the pragmatism in finding a compatible solution I still think SemVer is fine, and: - MAJOR should bump for *intentional* breaking changes—can't foresee unintentional ones like bug fixes - we should decouple marketing from tech versionning (e.g. named releases)

But I feel it's a cultural thing, rooted in fear ("I don't want to make it sound like a big thing for tech reasons") The current status quo is still not ideal 😄

Nice write up! I definitely wouldn't mind seeing you migrate your packages to this system. If I can comment on one thing: I think it will be easier to convince people to bump major versions more often rather than to adopt an extra "invisible" number. As you wrote, it's about human perception.

I really like the idea! But I just came across a side effect: `peerDependencies`. For example, I think UnoCSS used to be "unocss: ">= 0.31.13 < 1" and now it should be "unocss: ">= 0.31.13 < 100" I think is an elegant solution the use of 100s for "major" versions. It is compatible! 🤘

Glad you call out that pre-1.0 is generally a bad practice. Might want to do that sooner in the post for folks who might not understand that or why. Other than that, I think your new interpretation is fine. Doesn’t seem to me to be necessary as I don’t buy the “humans see it as log” thing.

Humans see things different depending on their perspective. For example, I don’t see anything less than skipping a few majors as “a large change”. Most of the packages I choose to depend on make small breaking changes slowly.

But to be clear I for sure see this as “semver compliant” technically which is great because back compat is critical. We don’t use http because it was the best of the best design after all lol.

While I understand the motivation, some of stated benefits and goals are lost along the way. Reasons why I don't think this is a good choice: - Adds complexity - Non-standard in a way that users lose the ability to infer what's going on with major version skips until they read an explanation

- If I'm on vue@65 and `npm i vue@66` fails then I might wrongly assume that v66 isn't released, and that I'm on the latest released major version. This may also break some tools which assume increments in major version by 1.

- v125 -> v200 is nice for showing significance however this doesn't hold up at larger numbers e.g. v1125 -> v1200 is arguably worse than v11 -> v12 I think going the other way around and removing complexity is a good approach and worth considering by collapsing patch into minor i.e.

major - intentional breaking change minor - unintentional breaking change (includes bugfixes) I think this is the most important distinction for users, is SemVer compliant, and doesn't require an explanation for users to upgrade versions intuitively

I think it makes sense for a mature-ish project when it can foresee minor breaking changes to happen often, and there's still value with v0 if that's uncertain, eg new projects. If not often, v1 still seems valuable to prevent number fatigue. So personally I wouldn't blanket adopt epoch semver 😅

Great post! Loved that you pointed out logarithmic thinking- something I’ve intuitively felt but not seen expressed 🙂

Brilliant strategy! With Epoch SemVer, spotlight game-changing transformations. Maintain clarity, ensure compatibility, and keep it user-friendly—all while staying in charge.

I'd rather see: 1. Normalize code mods. - This should be much more common; perhaps AI can make these easier to produce. - Tools auto apply Code Mods between your old and new versions. 2. Add a breaking indicator on the version. - Don't rely on Major/Minor changing to indicate breaking changes.

So it's the same as semver-with-many-majors and semver-but-keep-the-major-as-zero-so-that-every-minor-can-be-breaking (already a common practice), but with gaps in the major version to jump to 100, 200, 300. My take: tiny marketing win at the cost of awkward gaps in the versioning scheme, meh.

As a user of many packages and libraries, this is also not a problem I have with libraries, and may perpetuate a problem I do have. In my experience, 10 breaking changes are not easier to deal with when spread as 7 majors instead of 1. The only thing that is easier: fewer breaking changes.

Progressive breaking changes have one upside: you can try upgrading major releases one by one. It's still a painful process. Downsides: - flip-flopping changes (v11 changed an API, v16 changed it again, v21 yet again…); - encourages maintainers to give low guidance for breaking changes.

Of course maintainers who release many majors don’t HAVE to fall into these traps. But being able to release breaking changes often and in small increments allows hiding the extent of the practical instability of the software. I treat projects releasing 2+ majors a year as a red flag.

99 major versions ought to be enough for anybody 😂 In all seriousness - this is a great proposal 👏. Not sure if you're aware but you make a cameo appearance in my talk about API design on slide 31 because of I first heard about Epoch Semantic Versioning from you 🚀

tkdodo.euReact Query API Design - Lessons LearnedIn this talk, React Query maintainer Dominik will walk us through some of the API design choices that were made in React Query to get to that DX. You'll hear stories about things that went well, but a...

IMO this happens often in order to highlight the value proposition of updating in the face of breaking changes. If you just break stuff and don't offer something compelling in return, nobody updates.

I like the idea, and I'm happy to see your packages adopt this new scheme. Thinking about it in the context of Vite, we would have applied the first epoch jump from vite 1 to vite 2. Although we could argue that vite 1 should have never existed and remained as leading 0. When are the next epochs?

Maybe the next epoch change could happen once Vite moves to Rolldown and we remove all the backward compatibility layer we added to introduce Environment API. With these jumps, `{ EPOCH * 10 + MAJOR }` would be enough but already too close so 100 seems like a sensible choice.

I would still feel a bit uncomfortable with adopting this for Vite and making it jump from Vite 7 (?) to Vite 100. Even if it is more restrictive `EPOCH * 10`, feels more adoptable here. Eager to see how this new scheme works for your packages.

I keep thinking that `EPOCH * 10` could get the scheme more widely adopted. Given that any multiple of 10 would still have a big psychological effect no matter how this is defined, maybe it is fine to go with 10 instead of 100?

I really like the idea of differentiating major and minor breaking changes. I wonder if it's not too much to ask for a new npmrc setting that defaults to using ~ version ranges for installed packages that opt-in to "breaking minor versions" via some package.json field?