Semver has failed us June 19, 2014
As a maintainer of many popular packages such as express, I, along with the other maintainers, have come to realized that semantic versioning simply does not work in practice. It has caused a lot of bikeshedding, creating non-constructive discussions in the repository. It generally makes development actually more difficult.
Recently, some people have created some ideas about how to improve versioning.
I first proposed getting rid of versions
< 1 all together as
0.x.x modules are inherently unstable due to its lack of specification.
Damon Oehlman proposed slimver,
a stricter variant of semantic versioning.
I'm proposing ferver,
which changes the semantics of
semver to be more practical with breaking changes.
You can read more about it on the GitHub page,
but first, let's talk about the failures of semver.
Part of the major issues with semver is that
patchs could break.
A feature could be "fixed", but a consumer could have relied on that very
buggy feature, and patching it would break their app.
An example are times when a library simply does not behave
according to specifications and fixes it to cohere to the specification.
An example is with Express 3.4.3.
A bug with redirects was fixed, but some users were relying on that bug.
Thus, even though it was a patch (
it broke some people's apps.
A user asked to at least bump a minor version,
but if we strictly cohere to semver,
we can't because it's a patch, not a new feature.
I greatly sympathize with this user, and this particular case is essentially the first time I realized, "semver doesn't work".
0.x.x is anarchy
< 1.0.0 do not have semantic versions according to semver.
These are considered libraries "in development" and developers could version
however they see fit. Thus, developers bump the minor or patch numbers
however they like. It's anarchy.
The problem isn't that versions less than
< 1.0.0 are allowed.
Nope, it makes sense for libraries to be able to break changes before
declaring a stable
1.0.0. The problem is that there are no semantics
0.y.z versions. Consumers simply don't know how to depend
on these modules using version ranges
without introducing a significant amount of risk into their app.
The current solution to the above problems is to pin all dependencies.
However, this is absolutely stupid and annoying to me.
If you're pinning versions, you're reducing the package manager
to a glorified
It makes maintaining very difficult and annoying.
Duplicate dependencies are bad, especially in frontend development
where file size matters, which is one reason frontend developers prefer Bower's
flatter dependency directory vs. npm's nested.
When every library pins, you're going to have a lot of duplicate dependencies,
even if they're the same version!
Not everyone has the time to update every
patch and make a new release.
Even if you control the dependencies, some people like pinning dependencies.
To me, this is absolutely silly, but it is necessary because
patchs can break.
For example, if you look at the 2.x branch of Connect,
you'll see that all the commits are just dependency updates.
However, they all do not break backwards compatibility because
Connect coheres to semver.
These updates should not be necessary and should be available
just from typing
If you look at Express' current issues,
you'll see that half of them are planned for the next major version,
The problem with this is that these minor issues may be backwards incompatible,
but they are nevertheless issues that consumers would have to deal with
Update path regexp functionality would break routes
for a very few people who write really weird routes,
but it introduces many new features and provides better semantics.
This introduces a lot of benefits for most developers
while introducing risk to a very few developers.
Ideally, these changes should happen as fast as possible, but in a way that tells consumers, "Hey, this is new and improved, but it might break your app. Proceed with caution.". There's no way to say that with semver except with major version releases.
Semver does not have a good scheme for prereleases.
People append all sorts of weird strings to their versions.
1.0.0-220.127.116.11. Who knows what these mean.
It only makes libraries more difficult to consume as well as confuse consumers.
Since semver is liberal with these affixes,
package managers like
npm have trouble dealing with them.
For example, if you use
1.5.0-beta1 of a library and the latest
npm outdated will mark
1.5.0-beta1 as outdated.
Yeah, I don't think that's outdated.
A good versioning system would allow for prereleases and beta builds
while still cohering to
It would also be able to allow consumers to distinguish between
prereleases and releases semantically.
The fear of
Many developers never release
v1.0.0 of their projects.
With semver, this is really annoying because you have to pin
to reduce the risk of breaking changes.
But others absolutely hate when libraries update the major version.
They see it as a sign of "instability",
but according to semver, these backwards-incompatible changes
could be something as insignificant as returning
null instead of
which wouldn't break most people's apps.
The problem here is that people don't associate major versions with "breaking changes".
They associate it with the character, purpose, and philosophy of a library.
0.x.x means "We don't know what we're doing".
1.0.0 means "We think we know the direction of this library.".
2.0.0 means "We're changing directions a little bit".
3.0.0 means "We're changing directions a little bit, again".
Semver simply has the wrong semantics. Not every breaking change is a fundamental difference in a library's character. People are okay if you break things here and there, but it must be easy for them to know when you break something. This can only be done with a major version bump with semver.
There are two ways to solve semver: bump major versions often, or use a different versioning scheme.
Currently, I release most new modules I write as
1.0.0 and liberally
bump major versions. For example, koa-session
is already at
2.0.0, but koa hasn't even
Hell, co has already reached
3.0.6 and ES6 isn't even finalized.
This is why I proposed having semver drop versions
Who cares if you're at version
36 like Chrome.
I just want to know if something would break!
But this is not suitable for most people since, due to semver's semantics,
they correlate a lot of major version bumps with the library being "unstable".
The other solution is just use a different versioning scheme. This is what ferver - versioning based on whether a change is breaking. Please, don't use it though. It's only a thought.