Β· 14 min read
Give Marketing Their Own Digit

My proposal: 4 digits are GEN.COMPAT.MAJOR.PATCH, or lean 3 digits COMPAT.MAJOR.PATCH
Stop! If you don't know the laws of Semantic Versioning 2.0.0, go skim them: SemVer.org.
In May 2026, Bun shipped version 1.3.14. It was a lovely little patch release, as Bun patch releases go: a new Bun.Image API, a new global virtual store for the isolated linker, HTTP/2 and HTTP/3 work. You know. Fixes.

The next day, a pull request merged into Bun's main branch. PR #30412, six thousand seven hundred and fifty-five commits, one million and nine thousand lines added, titled β with the serene confidence of a man who has stopped worrying and learned to love the unlimited token budget β "Rewrite Bun in Rust." The entire runtime, ported from Zig by a swarm of Claude agents β Anthropic bought Bun in December 2025, and the token meter runs on the company card β in six days, from a branch literally named claude/phase-a-port. The largest pull request in GitHub history, reviewed by claude[bot] and coderabbitai[bot].
The version number for all this?
1.4.0. A minor.
It is allowed. Under a strict reading of SemVer, an API-compatible change is never requiredto be a major, no matter how many lines it touches. The tests pass (99.8% of them). The public API didn't move. Honestly, the same laws allowed 1.3.15too, and that's the version I was rooting for: the whole runtime swapped out in a patch.
The digits stopped meaning things a while ago
I adore Bun. Jarred Sumner and the Bun team are relentless, generous shippers. But their changelog is a museum of version digit crimes:
- 1.2.9 β a patch β shipped a built-in Redis client. An entire database client in a patch.
- 1.2.21 β a patch β added MySQL support to
Bun.SQL, native YAML parsing, and an OS-keychain secrets API. - 1.3.8 β a patch β introduced
Bun.markdown, a built-in Markdown parser. - 1.3.11 β also a patch, seven weeks later β broke
Bun.markdown's render callback API. The release notes say "Breaking change:" right there, mid-paragraph, like a shrug.
An API was born in a patch and broken in a patch, and the minor digit never moved.
And no, this isn't me waving a fist at the clouds. The clouds have Terms of Service: npm's own docs ask every publisher to follow the SemVer spec, "to keep the JavaScript ecosystem healthy, reliable, and secure." The registry assumes and depends on this: npm install saves your dependency as ^1.3.8 by default, a caret range that will auto-accept every future minor and patch sight unseen, on nothing but the publisher's digits. Publishing a package is signing that contract. A breaking change in a PATCH doesn't wait politely to be adopted β it deploys itself through a million caret ranges by the next morning's CI runs. Versioning is unglamorous, load-bearing software engineering, one of the disciplines that separates the title engineer from developer. A vibe-coded weekend project is free not to care, and shouldn't. But the moment npm publishruns, someone else's lockfile starts trusting you.
Python, meanwhile, has been running the same scam with more dignity and for much longer. Python 3.13 removed nineteen standard-library modules. In a minor. They do this every year β deprecations ripen, the reaper comes, and the version goes from 3.x to 3.x+1 like nothing happened. PEP 2026 β which proposed switching to calendar versioning, and was rejected β is refreshingly honest about it: "People often assume Python follows SemVer and complain about breaking changes in feature releases." Python's scheme predates SemVer by fifteen years. The complaints predate most of the complainers' careers.
And the leading Python 3? It hasn't moved since 2008 and probably never will due to the decade-long 2β3 migration trauma. The 3 has transitioned from engineering digit to memorial.
The diagnosis: one number, three masters

The leading number is asked to indicate and communicate three completely different things:
- The contract β will this break my code? This is what
npmandcargoand your lockfile care about. It's the only part a machine can check. - The significanceβ is this a big deal? Engineers rewrote the runtime; they'd like the number to reflect that they did a thing.
- The brandβ is this a new era? Keynotes, launch posts, sales decks. "Version 2" moves customers. "Version 1.4.0" does not.
SemVer 2 gives these three masters one digit to fight over, and it has predictable casualties. Anthony Fu pointed outthat humans read version numbers logarithmically: 2.0 β 3.0 feels seismic, 125 β 126 feels like Tuesday. So maintainers hoard breaking changes to make a major "worth it" β which makes majors huge and scary, which makes everyone else avoid bumping them at all, which is how you get breaking changes smuggled into minors and Redis clients smuggled into patches. Dominik Dorfmeister compressed the whole pathology into a single line:
"Breaking Changes !== Marketing Event."
The fix: one digit per master
Three numbers for most software, with a dedicated backwards-compatibility digit in front:
COMPAT.MAJOR.PATCH
and a fourth, up front, for projects that have a marketing department to keep fed:
GEN.COMPAT.MAJOR.PATCH

Looking for MINOR? Cut. MAJOR and MINOR were always the same digit wearing two hats β compatible additions, sized by feelings β and one vibes digit is plenty. And if a fast-rolling MAJOR worries you: the spec sets no ceiling at all, and npm's parser accepts any digit up to JavaScript's Number.MAX_SAFE_INTEGER, so the MAJOR versioning scheme tops out at 2.9007199254740991.3. The vibes digit will not run out.
At one compatible release per second, that's roughly 285 million years of runway.

One rule in this whole scheme is load-bearing, so it gets capital letters:
THE HARD RULE. If a release breaks backwards compatibility, COMPATgoes up. No exceptions, no vibes, no "it only affects an edge case."Compatibility is the one property a machine can check. SemVer's MAJOR conflates "breaking" (checkable) with "big" (vibes), so no tool can own it. COMPAT is onlythe contract, so a CI gate can diff your public API and refuse to publish a lying version. This isn't hypothetical: Elm's package manager has computed version bumps from API diffs for years and simply won't let you fib. Rust has cargo-semver-checks, Go has apidiff β Go even puts the major version in the import path, because a different contract is a different package. Dynamic languages blunt the robot β most Python and JavaScript breaks are behavioral, invisible to an API diff β so there COMPAT falls back from proof to promise. Still: a far narrower promise than SemVer's MAJOR ever made, and narrower promises get kept.
The lean base is a legal SemVer string today. 2.47.3 parses everywhere. Publish it to npm this afternoon and ^2.47.3means precisely "anything that doesn't break me" β the caret finally does what every developer always wished it did β while ~2.47.3 floats fixes only. The migration path is zero meters long.
latest β *.*.*
^1.2.3 β 1.*.*
~1.2.3 β 1.2.*
1.2.3 β 1.2.3
And the other digits? Now that they carry no contract, they're safe to be human about. Is the Bun Rust rewrite a MAJOR or just a very confident PATCH? Argue in the team channel!
The GENERATION digit
The Linux kernel has been running one since 2011. Kernel majors are officially vibes β 3.0 shipped for the project's twentieth birthday, 4.0 came from a Google+ poll, and 5.0, per Linus himself, "doesn't mean anything more than that the 4.x numbers started getting big enough that I ran out of fingers and toes" . The digit is inert, everyone knows it's inert, and a series ends whenever the minor starts to feel heavy: 3.19 β 4.0, 4.20 β 5.0, 5.19 β 6.0, and β this April β 6.19 β 7.0. Only one series ever counted all the way to twenty. The machine this post was written on runs kernel 7.0.14-201.fc44.x86_64.
Watch it work on our patients:
Bun.The ZigβRust rewrite is enormous and β per the test suite β compatible. That's the textbook MAJOR: MAJOR goes up, zeros to its right. And when Anthropic marketing wants to announce The Rust Era? Bump GEN, and there's a big round 2 for the keynote slide. The version number finally tells the truth, and everyone got their press release. The Redis client? A MAJOR too. The Bun.markdown break? COMPAT++, everything right of it to zero. Spend the digits freely β they're cheap now.
Python. Already running this scheme in a trenchcoat: 3.13.4 is GEN.COMPAT.PATCHβ a frozen brand digit, a yearly breaking digit, a fixes digit β just mislabeled with SemVer's names, which is where all the complaining comes from. Relabel it and every complaint dissolves: COMPAT goes up each October with the deprecation reaper, and whether "Python 4" ever ships becomes explicitlya marketing decision. Which it already is. We'd merely stop pretending otherwise.
Windows. The example everyone already knows. The number β 8, 10, 11 β is pure GEN, and it moves on keynote logic: 9 got skipped entirely (the popular story being that decades of lazy code in the wild checks whether the version string starts with "Windows 9"to detect 95 and 98 β arguably history's funniest COMPAT incident), 10 was announced as "the last version of Windows" right up until 11.
Microsoft Windows 11 [Version 10.0.26200.7623]
Fully patched Windows 11 reports itself as version 10.0β NT major, NT minor, then a build number, then a revision that ticks every Patch Tuesday. The marketing digit and the engineering digits haven't agreed since 2015, and before that it was even better: Windows 7 was NT 6.1, Windows 8 was 6.2, and 8.1 was 6.3, because so much software had version-checked the 6 that bumping it broke apps. Microsoft's internal major was frozen for three consecutive marketing releases by the weight of other people's assumptionsβ behaving exactly like a COMPAT digit, owned in practice by every app that ever sniffed it. When they finally jumped to NT 10.0, they had to deprecate the old version API and quietly tell un-manifested apps "6.2, forever." Hyrum's Law, meet enterprise software.
And if five numbers still sounds exotic: Android ships this exact split: "Android 16" is the marketing number; API level 36 is the compatibility number your app actually targets. Different numbers, different owners, zero confusion. Rust's editions(2015, 2018, 2021, 2024) are GEN with a compiler flag. The industry keeps independently reinventing "give marketing their own number" β the only innovation here is putting it in the same dotted string as everything else.
In one dotted string: Rust 1.90.0 on the 2021 edition is Rust 2021.1.90.0; opt the same crate into the 2024 edition and it's Rust 2024.1.90.0 β a pure GEN update, every engineering digit untouched.
The tail is yours

GEN.COMPAT.MAJOR.PATCH[-PRERELEASE][+BUILD] 3.2.4.1-rc.1+20260707.9f3ab12
-rc.1 is the channel β rc, beta, canary β and orders before the release. +build is the receipt β timestamp, CI run number, git SHA β and never affects precedence. Teams that want TrunkVer-style timestamps or embedded SHAs bolt them on here.
Objections, pre-argued
"npm can't parse four numbers." Correct β the lean form needs no permission: COMPAT.MAJOR.PATCHis a valid semver triple right now, with correct caret semantics. And if you want GEN inside three-digit tooling too, do what Anthony Fu's Epoch SemVer does and collapse the front: {GEN*1000 + COMPAT}. UnoCSS already lives like this. Haskell's PVP has run a four-number scheme with a two-part major since 2006 β three years before SemVer existed β so "the ecosystem could never" is empirically false; the ecosystem just has to want to.
"Four numbers is still a lot." Most projects need three. The fourth digit β GEN β is opt-in. A quiet library that never breaks anyone is 2.2845.12 forever, and its COMPAT digit sitting eternally at 2 is the best marketing it will ever have.
"You renamed MAJOR."Guilty. "Major" has meant breakingsince 2009, and during the transition every "we cut a major" will need a footnote. But the weld between the prestige word and the contract digit is the disease β it's why breaking changes get hoarded to make a major "worth it" and why everyone downstream dreads the number moving. So split the custody. The robot takes the boring name: nobody hoards breaks to make a COMPAT bump feel earned, and nobody has ever keynoted one. Engineering keeps the glamour word, now meaning exactly what every engineer always wanted it to mean: we did a thing. And in the lean form the contract still holds the leftmost slot, so when old habits call a COMPAT bump "a major bump," they're right where it counts β the caret agrees.
"MAJOR numbers will get huge." They will β a busy project could hit 50 in a year, Chrome-style. But we already established that humans read big numbers logarithmically and stop assigning them meaning, which is exactly what we wantfrom an engineering digit. The moment nobody's proud of the number, the number starts telling the truth.
"MAJOR versus PATCH is still vibes." On purpose β the scheme moves judgment to where misjudgment is cheap. The call that detonates lockfiles, does this break anyone, belongs to the robot now. The call left to taste, is this announcement-sized or fix-sized, has a worst case of a ~ user receiving a free feature instead of a fix. A filing error, not an outage.
"Marketing will bump GEN constantly."Let them! It's inert. That's the trade: they get a sandbox with a big shiny number in it, and in exchange they stay out of the digits that mean things. Everyone who has ever shipped software gets a distant look at this point, like a war veteran hearing about peace.
"Even patches break someone." Yes β Hyrum's Law is undefeated, and a million-line rewrite is "compatible" the way a replaced ship of Theseus is "the same boat." No digit can encode risk; digits encode declared intent.
An invitation
So here's the actual ask. Not that Bun adopts this, not that Python renumbers anything β but that we, the hive mind, admit what we all already know: the digits are lying because we gave three departments one number to share. Separate the powers on new projects.

GEN.COMPAT.MAJOR.PATCH is the base to build upon. Call it SemVer 3 β a major bump that is, for once, perfectly honest: the fourth digit breaks every SemVer 2 parser on earth.
In this SemVer 3, Bun's 1.3.11 would have been 2.0.0 β the break announced in the digit, instead of mid-paragraph, with a shrug.
Does the digit really matter that much? Watch trust leave in real time. Last October, Dax β creator of OpenCode, an agent CLI with millions of users, built on Bun β tweeted "i am bun's #1 fan name a bigger fan of bun than me." By February, OpenCode had agents running in a loop, opening one PR per file, migrating off Bun's APIs β the Rust-rewrite technique, pointed the other way. The stated reasons are the punchline: Windows stability pain, "we're not certain about the future of bun," and a benchmark verdict that Bun's celebrated speed wasn't actually buying them much. By April, zero Bun-specific APIs remained, and OpenCode can now run entirely on Node β the slower product with the boring LTS lines, where a patch is a patch, every time, for thirty months. The #1 fan traded performance for predictability, and the trade wasn't close. The Bun acquisition post had said the quiet part out loud β "If Bun breaks, Claude Code breaks" β and everyone who isn't Anthropic did the math. If Bun ever wants to become trustworthy software, the fix costs nothing at the keynote: never again ship a breaking change in a PATCH. Put the contract in COMPAT, let the robot guard it, and ^becomes a promise even your owner's competitor can build on.
Sources & further reading: Bun's blog Β· Windows 11 release information Β· the rewrite PR coverage Β· OpenCode's migration off Bun Β· Bun's own unsafe-block audit Β· PEP 2026 Β· Epoch SemVer Β· Haskell PVP Β· SemVer will not save you Β· Major version numbers are not sacred Β· a comprehensive list of versioning schemes