let me be clear, i don't think semver is necessarily bad, i just don't think numerology has any place in dependency management
let's talk about some problems we face with packages (problems outside of "people complain at me on github when their build breaks") and let's talk about what part semver plays in solving them:
- problem: are these two packages the same
- does semver solve this problem? no.
- answer: we use a hash of the package as the identifier
hold up. don't open that browser tab to complain about my post just yet. give me a second to explain myself. yes, this might seem self evident, and some of you might say "semver isn't meant to be used here", but semver does promise that "different packages use different versions"
unfortunately, not everyone lives up to the promise, and so the better way to solve this is by using hashes, or if you will, content addressable identifiers. hashes work a lot better for "are these two things the same"
next problem.
- problem: how stable is this package?
- semver's answer: here's a tag, go fuck yourself
- actual answer is usually: release candidates, release channels
a problem every library author faces is thus: this package is not ready yet, but i won't know if it is ready until people install it
the usual approach to handling this is "release candidates",and sure enough, you can have 1.2.3-staging or 1.2.3-rc2 in semver, but they're kinda slapped on the side without much care. it's someone else's problem.
another approach is something familiar to debian or ubuntu users, having stable, unstable, and testing branches of your system, with packages getting promoted up the chain as they go through testing.
release channels let you convey things like "this is development" or "this is long term support", and almost every large software project has some notion of it, even if they all use slightly different names.
it'd be nice if release channels were part of the ad-hoc package specifier language that semver defined, but life is suffering, so they are not included. that's why we can't have pre-release packages either
we do get v0.x.y as way of saying "good luck buddy" but that's a whole other can of worms we'll have to get to later.
- problem: which of these packages is newer
- semver's answer: bigger number unless it's tagged
- meanwhile everything else is just uses a timestamp
again, i must ask you to hold up: i'm doing a bit here. if you think about it for a bit, semver is just a logical clock for packages, or more precisely a tuple of three logical clocks with a defined ordering.
on the other hand, we could just as easily use a physical clock for versioning. we could keep a tuple of three dates, (date of last compatible version, date of last commit, date of last build), and although the version numbers would be a little unwieldy, we'd never have to think about them.
it's not totally baroque: in practice many places tie their version number to physical time, incrementing it every year, as there isn't really much advantage in trying to keep it as low as possible.
it's impossible to deprecate things otherwise, especially under semver
- problem: how do i deprecate things
- semver: if it's 0.x.y, go hog wild, otherwise increment major number (ps you're a bad person)
- everyone else: either we ratchet the number up every six months and get over it, or we act as if our javascript library has an eight year support contract and never leave 1.x
again, i don't hate semver, but here is the bit that brings me the most ire: semver offers you three choices for major version numbers, and none of them aid you in deprecation.
- option 1: stay at v0.x, everything breaks all the time, don't worry
- option 2: stay at v1.x and occasionally think about v2 but not yet
- option 3: you are version 87 now and will be on 112 by next quarter, no-one cares anymore, and the support promises of semver are meaningless.
most projects start with option 1, try out option 2, then move onto option 3 if they have any self preservation instincts. people will complain about it, but browsers have finally convinced most developers that version numbers are a terrible substitute for feature detection.
it's worth noting that things like feature flags, or feature gates, are a really popular way of bringing in experimental code without breaking things, but at the same time, they don't directly help you deprecate things, they just help you avoid it by slowing down all the new features.
the thing is, sure enough, not many things help with deprecation, but semver encourages developers to avoid it, and trains users to expect it infrequently. as a result, i think semver hasn't really been of much benefit.
so, take a deep breath: it's not that i hate semver, but
- version numbers are lousy means of feature detection
- physical timestamps are often more useful than logical ones
- you need release channels to handle different expectations of support and stability (like betas, release candidates, or long term support)
- a hash is better than a version number for identification
- and "if you increment the major number, you're on your own" leaves us back where we started
but there's just one last thesis to nail to the door: packages often support more than one version of an api, at the same time.
you could have a library that supports "v1" "v2" and "v2-alpha", and you don't have to look far to find an example of it. kubernetes is everywhere these days, and serves as one of many counterexamples to semver's "a package supports exactly one version of an api" mentality
you might be asking why this makes me so mad, but this is precisely why deprecation is so hard under semver. if a package could support more than one version number, deprecation would be easy.
just overlap version numbers. "v1.7" and "v2.0" are the same, and you'd carry on for a few minor versions until you're done with the old api.
and, well, this isn't impossible in semver: you can publish the same package under two different versions, but now you have two problems. the tooling just doesn't like the idea that the same file is two different packages much.
oh well