Live data from Hacker News

SemVer has failed us

jongleberry.com

11–20 of 52 posts

Re: SemVer has failed us

#11
You had me until "Duplicate dependencies are bad". The reason every package manager except npm and Apple's bundles are a piece of shit is because they attempt to install random dependent garbage globally and by default.

Re: SemVer has failed us

#12
A core problem is that the current system conflates two subtly different notions of version: the low-level package manager version (is it backwards compatible? will it build?) and the high-level person version (is this the same library with some changes, or is it a new generation or significant redesign?).

The first notion is needed for keeping our code stable but keeping up with bufixes and security patches. The second one is needed to deal with design issues: will this change require a lot of reworking? Will it change the underlying model or abstraction in the library?

Both of these are important. But because Semver requires bumping for any breaking changes—even if they are semantically minor—it can only address the first, low-level notion of versioning, not the second.

A possible solution? We can have two minor version numbers:

    4.1.0.2
Patches and backwards compatible changes work as always. But if you are making a breaking change, you have two options. If it's not too big, just increment the first one:

    4.2.0.0
But if you're really changing things up and creating a new generation of your library, go for

    5.0.0.0
Of course, the difference between the two major numbers is not well-specified. But that's fine! From the more technical, low-level perspective, it's no different from normal Semver: any change in the major version (either of the two numbers) can be breaking. It just also lets library authors send an additional signal to developers about the scope of the change.

For example, this scheme gives more information on how "stable" a library is. If the second major number is high, it means there are a lot of minor breaking changes; it would take a bit of active maintenance to keep up, but as long as the first major number is not changing, these steps won't be radical.

Personally, this approach makes a lot of sense to me. It's also more or less what Haskell packages use according to the "package versioning policy", and it seems to work pretty well. (There are some other pervasive issues with Haskell version numbers, but not related to how they treat major versions.)

Re: SemVer has failed us

#13
I think the root problem is that SemVer works great, and was designed for, larger projects with many developers and a relatively stable API.

For smaller projects and libraries, it works very poorly, because these are usually "itch-scratching" efforts that don't necessarily have a clear endpoint or goal. For these kinds of packages, I wish people would just use a commit number. I really hate having to agonize over SemVer for a 500 LOC miniproject.

That kind of project, incidentally, is the most likely to be in "0.X.X" limbo forever. See for example the Clojure ecosystem where 80% of packages are this way.

Re: SemVer has failed us

#14
post #9
post #5

Completely agree. For a long time, I've thought that humans shouldn't be in charge of version numbers at all. The main problem is that a human isn't reliable at determining "is this change breaking?". Unfortunately our software isn't well-specified enough to allow the computer to determine version numbers automatically yet. To make this a reality, you'd need Haskell-level type safety, along with specifying other cons…

> Unfortunately our software isn't well-specified enough to allow the computer to determine version numbers automatically yet. To make this a reality, you'd need Haskell-level type safety, along with specifying other constraints, like "this fn is O(log(n)) or better". Isn't this going to be impossible for the same reason the Halting Problem is unsolvable?

[deleted]

Re: SemVer has failed us

#15
post #9
post #5

Completely agree. For a long time, I've thought that humans shouldn't be in charge of version numbers at all. The main problem is that a human isn't reliable at determining "is this change breaking?". Unfortunately our software isn't well-specified enough to allow the computer to determine version numbers automatically yet. To make this a reality, you'd need Haskell-level type safety, along with specifying other cons…

> Unfortunately our software isn't well-specified enough to allow the computer to determine version numbers automatically yet. To make this a reality, you'd need Haskell-level type safety, along with specifying other constraints, like "this fn is O(log(n)) or better". Isn't this going to be impossible for the same reason the Halting Problem is unsolvable?

The halting problem is unsolvable in the general case, but there are lots of specific, practical cases where you can answer "yes, this program terminates".

Is it possible to specify the complexity of every arbitrary function? Probably not. Is it possible (and useful!) to specify the complexity of most functions you use on a daily basis? Absolutely.

A stronger critique I heard once is that 'computer generated complexity analysis has too much noise in it', where you ask what is the complexity of this fn, and rather than giving you O(n^2), it gives you back O(n^2 * m * q * log(r) * s^4), but it turns out 's' is "always" a small number, and m & q are constants. You can improve (but not completely eliminate) this by doing empirical testing, i.e. "the statistical sample of 1000 test runs varying the input does appear to fit an O(n^2) curve".

Re: SemVer has failed us

#16
post #5

Completely agree. For a long time, I've thought that humans shouldn't be in charge of version numbers at all. The main problem is that a human isn't reliable at determining "is this change breaking?". Unfortunately our software isn't well-specified enough to allow the computer to determine version numbers automatically yet. To make this a reality, you'd need Haskell-level type safety, along with specifying other cons…

For anyone in the Haskell world, it's obvious that semantic versioning[1] doesn't work. Some non-trivial amount of that is likely due to how horribly broken Cabal is, a problem the community remains in staunch denial of. But for the rest, one problem is that because Haskell more thoroughly specifies types, individual functions can remain the same in behavior but change in type because a bug-fix, say, fixes a record type used somewhere. The external interface might be largely the same, or even identical, but the binary output is non-backwards compatible. Dynamic libraries and strong, descriptive type systems aren't very compatible. The result is that a lot of bug fixes become minor version bumps.

These minor version bumps scare library developers, especially people that produce libraries that depend on libraries that depend on... and so on. So packages fail to build too often because of constraints. So the irony of this thorough process for package building is that humans have mucked it up and made it break builds. In almost every case in which semantic versions blocked some update from occurring and caused my work to stop, it was because I needed to bump the constraints on some third-party package. It's a boring process that I've only become too accustomed to:

1. try to build with updated libA 1.1.0-foobar

2. libB depended on libA 3. download source for libB

4. update constraint and repackage libB locally

5. rebuild main application

This happens just about every time there's a new version of any major piece of Haskell software.

If we really want to move into The Future, we need to start versioning individual modules or functions, instead of whole suites of software. But the cognitive burden of doing this is very high, and the software to do it hasn't been written yet.

[1] The Haskell community doesn't technically use semver, but rather a related policy called the package versioning policy. That's a nitpick though, and doesn't affect my argument.

Re: SemVer has failed us

#17
post #15
post #9

Earlier quoted context omitted.

> Unfortunately our software isn't well-specified enough to allow the computer to determine version numbers automatically yet. To make this a reality, you'd need Haskell-level type safety, along with specifying other constraints, like "this fn is O(log(n)) or better". Isn't this going to be impossible for the same reason the Halting Problem is unsolvable?

The halting problem is unsolvable in the general case, but there are lots of specific, practical cases where you can answer "yes, this program terminates". Is it possible to specify the complexity of every arbitrary function? Probably not. Is it possible (and useful!) to specify the complexity of most functions you use on a daily basis? Absolutely. A stronger critique I heard once is that 'computer generated complexi…

While it may be possible to statically analyze time complexity of code, I'm not sure it will ever be a good idea to go whole hog and generate version numbers from these analyses due to the fact that it's never going to be 100% accurate.

Re: SemVer has failed us

#18
Personally, I would far prefer a strong contracts system over semantic versioning. The ability to say "I rely on these N interfaces to present accept these argument types and return these types, and here are some test arguments and the values I expect returned" to be a much better way of describing what you rely on. Even better is that by defining things in terms of contracts, you should be able to replace dependencies with any module that matches the contract you require.

In general, it's always better to have a computer enforced standard over a human enforced one. Semantic versioning can just become human readable sugar over a set of contractual expectations.

Re: SemVer has failed us

#19

SemVer is working fine, you just need to face up to the real impact of a change on clients, not what you wish was the impact of the change. If a change breaks clients who rely on buggy behavior, you need to bump a version, not a patch number.

This may be true, but in practice (as pointed out in the article), EVERYTHING can potentially be a breaking change (http://xkcd.com/1172/). Since this is the case, you either need to always bump the version number (rendering the patch number useless), or bump the patch number but then have to wait for the bug reports to come in... once you get one bug report stating your patch broke someone's code, you would bump the version number... this doesn't seem practical, however.

Re: SemVer has failed us

#20

We've used SemVer for Julia for a while now and it seems fine. However, versioning and compatibility are still hard problems and a perennial, unavoidable pain. The way I like to think of it is this: - patch: forwards and backwards compatible – only bug fixes, no new features, no changing existing features. - minor: backwards compatible – new features allowed, but old code should keep working. - major: all bets are of…

The argument in the article seems strange to me: "downstream relied on undocumented behavior, and now that behavior has changed, so downstream breaks"

So what should upstream do? After all, the premise must be that the upstream does not control or even know about all the clients! The entire point is to separate the concerns, so how could upstream know if a bug fix is a breaking change if it passes the internal regression tests?

Of course they can't know without asking downstream, and hence the use of release candidates.

Post reply on HN