
Regarding Semantic Versioning - zdw
https://www.danielmoch.com/posts/2020/09/regarding-semantic-versioning/
======
swyx
bit of a rant, but all very reasonable. in general though, i find that people
who get this upset about semver tend to view the "contract" of semver to be an
ironclad one (right down to being automatically determined by the package
ecosystem rather than by the maintainer).

I am much more persuaded by the argument (not mine, i get this from more
experienced maintainers) that semver is a _social_ contract, because what
constitutes a bugfix vs a new feature vs a breaking change is exactly as well
defined as "what is this piece of code really supposed to do"? aka it's not
well defined at all. all software "contracts" are underspecified, given enough
users and scale.

remember Hyrum's Law: "With a sufficient number of users of an API, it does
not matter what you promise in the contract - all observable behaviors of your
system will be depended on by somebody." Even bugs will be relied on and
people get pissy about semver when you break them!

~~~
lhorie
When you think in terms of semver as a social contract, one can also take into
consideration what the documentation says. If the docs don't explicitly say
you can do something, then it's entirely reasonable for the library author to
say "well, that's relying on undefined behavior, you should not do that".

~~~
simonw
This is why when I'm building APIs or libraries that are meant to be used by
developers I won't consider a feature "done" until it's both tested AND
documented.

The documentation is the contract that I use for semver. If something is
documented, I can't break it without bumping the major version number. If it's
an undocumented internal class or function I can change it any time I like.

~~~
ithkuil
Interesting things happen around this "undocumented" cases.

For example, imagine you expose a "map" API and you explicitly don't mention
it's a hash map or some tree. You also don't claim the the iterator will be in
order, but you fail to document that you explicitly don't promise any order at
all.

Some users will not read the documentation and say "wait a minute, it doesn't
claim whether any ordering is guaranteed nor it claims the contrary, hence I
must assume it's undefined behaviour thus I can't rely on any ordering".

Instead some users will look at the behaviour and assume the documentation
just forgot to mention that aspect (and if the implementation indeed iterates
the map in order they'll just assume that's what the documentation should have
written).

After all, just as code, documentation can have bugs and will be amended in
subsequent releases.

Now, you finally realize that you forgot to say "the iteration order is
undefined" and make a new release with that documentation fix. The question
is: would you consider this a major "breaking" change or a minor point
release?

------
sz4kerto
I don't like semver.

If the library follows the rules, then...

Separation of minor and patch versions doesn't make sense. Both in the case of
a minor or patch version change the client should be able to upgrade to the
latest version.

A major version change means that backwards compatibility is likely broken. In
that case, we're actually talking about a new library.

Therefore you can just use a single integer as the version number, and if you
break backwards compatibility, then you change the library name. If you had a
library called MyLib with version 1.0.0, then you can just go with version 1
instead. In case of a patch (no functional change), you go to version 2, and
all clients can immediately upgrade. Then if you add a new feature, but
backwards compatibility is preserved, you can go with version 3, and all
clients can immediately upgrade. If you break backwards compatibility, then
you rename your library to MyLib2, so it's clear to everyone that this is a
new library (with similar functionality).

The real reason for minor and patch versions is that libraries actually don't
follow semver, and patch version change means 'small change, don't worry about
re-testing', minor version change means 'not that small change, you should re-
test' and major version change means 'we've very likely broken most
dependencies'.

In real life, you need to re-test anyway.

~~~
dragonwriter
> Separation of minor and patch versions doesn't make sense.

Yes, it does.

> Both in the case of a minor or patch version change the client should be
> able to upgrade to the latest version.

Yes, but if it's a minor version change then, as a developer, I _also_ want to
review the changelog for new features I should consider whether we should add
backlog items for refactoring to take advantage of, while a patch doesn't
create that need. There is more space for SemVer-aware tooling than “is this
safe to update”, though that's obviously the most important single piece of
information.

> The real reason for minor and patch versions is that libraries actually
> don't follow semver, and patch version change means 'small change, don't
> worry about re-testing'

No, you always worry about retesting because SemVer is a statement of API
intent, but does not include a guarantee against bugs, including new or
modified bugs.

~~~
sz4kerto
> No, you always worry about retesting because SemVer is a statement of API
> intent, but does not include a guarantee against bugs, including new or
> modified bugs.

That's what I do, yes. What I tried to explain is how library devs generally
update versions.

> Yes, but if it's a minor version change then, as a developer, I also want to
> review the changelog for new features I should consider whether we should
> add backlog items for refactoring to take advantage of, while a patch
> doesn't create that need

This sounds like a good argument; although in my professional experience I
found that 1) I need to re-test anyway 2) I need to read the change
description anyway (as bugfixes most of the time mean behavioral changes, so I
want to understand why the patch was necessary).

So overall I understand the theoretical argument behind semver, but I still
don't see a very strong argument behind it. At least not as strong argument as
popular semver has become.

------
tasuki
Rich Hickey, the creator of Clojure, has a wonderful talk where he made a case
against Semantic Versioning:
[https://www.youtube.com/watch?v=oyLBGkS5ICk](https://www.youtube.com/watch?v=oyLBGkS5ICk)

------
papaf
In theory it should be possible for a package manager to check the public API
for obvious breaking changes and throw an error if the major version has not
changed.

Its been done as a research project [1] but I don't think it has been done in
any major language or package manager.

[1]
[https://drops.dagstuhl.de/opus/volltexte/2018/8456/pdf/OASIc...](https://drops.dagstuhl.de/opus/volltexte/2018/8456/pdf/OASIcs-
ICLP-2017-6.pdf)

~~~
erikw
Elm enforces this since 2014 at the package manager level. They are able to do
this because since the language is pure, the compiler knows if a function's
signature has changed. If you are changing implementation details that don't
affect data transformation, then that would just be a dot release. I haven't
actually published an Elm package, but from what I've read this seems like a
great setup.

~~~
rabidrat
Function signature is only half the battle. The semantics can change without
the signature changing. This could be caught by a unit/functional test, but
then you'd have to have 100% semantic interface coverage. Which is not quite
worth it for most libraries/packages.

------
try-try-again
semver is a nice concept, but it doesn't work in practise. If it did then
yarn.lock and the npm equivalent wouldn't be needed. But clearly locking in
exact versions of modules is the only sane way to have reproducible builds.

~~~
rgoulter
I tend to see "reproducible" used to mean things like what nix/guix aim for.
SemVer is of course insufficient for that, but never promised to be.

In terms of "this set of semvers can be satisfied in a way that the project
will always build", yeah, it'd be nice if semver could be used like that
rather than as a rule-of-thumb.

------
grzes
thats why i pin my deps to the exact versions in production apps :)

