1. 9
  1.  

  2. 25

    Why do people overthink this? Versions are for human consumption. If I use x.y.z and x.y.z+1 comes out, my expectation is that I should be able to upgrade (if I need to) with minimum friction. Sometimes (rarely!) this is not the case. Tough life.

    Similarly I expect I should be able to upgrade to x.y+1.zz, but in this case I expect there might be more work involved, more testing, etc. In general, it should still work though. If not, tough life.

    I fully expect moving to x+1.yy.zz would be painful. Sometimes it isn’t though. Life is great.

    What’s the problem? The version communicates information to me. Like every other communication, sometimes it’s not perfectly accurate. So what? it’s news, not math.

    It seems that people who complain about this are the people who want to upgrade without testing. That is insane. You always need to test. You can’t trust that it will work because some guy who doesn’t know how you use the software promised you that it will work. No, he promised it should work. There are no guarantees. You always need to test.

    1. 5

      You are missing one final thing. I expect to be able to install X and X+1 side by side in whatever system. So many things seem to miss that, at least vgo gets that right.

      1. 2

        Yes, unfortunately almost every package manager gets this wrong.

        1. 3

          In a lot of cases it’s not the package manager’s fault; it’s the way the language does module loading. npm gets this right but the only reason it’s able to is because Node’s module loading algorithm supports (was designed to support?) this usecase.

          1. 1

            They were written at the same time by the same person, so yes, “designed” is appropriate.

            1. 1

              Do you mean require() and npm? I don’t think that’s right. I’m just assuming require() was Ryan Dahl in the very beginning of Node (docs say 0.1.13). And npm was by Isaac Schlueter, quite a while after that. People used to share Node modules on the Node GitHub wiki, and IIRC (though I wasn’t there, I just know from reading) npm was one of several package managers at the time.

              1. 2

                I was told that Isaac implemented both; it’s possible that I was misinformed, or maybe it was re-implemented by him.

          2. 1

            Package managers that allow side-by-side global installs that I can think of:

            • gem / bundler
            • maven
            • homebrew
            • nix

            They all require special tools to choose which version you want to use though. Are there any others? Are there any without that requirement?

        2. 1

          After accepting that every upstream change can break our code the next step is to accept that additional “level of probability” communicated by those dotted numbers are useless: they don’t affect your behavior as a maintainer, you still have to a) read and understand what changed and b) update and test your system. Which means that a single number would do just nice.

          I can speculate that semver became popular because of people who want to be trendy by “living on the bleeding edge” but still want some escape hatch that would “allow” them to not really read and understand all change logs of all those dozens of dependencies changing daily. So they like semver because if anything breaks after a minor version change, they can say it’s not their fault.

          1. 1

            It seems that people who complain about this are the people who want to upgrade without testing.

            Unfortunately, I’ve found that most people setup their package dependencies in whatever system to take X.*.*, so you automatically get updates between builds. That isn’t necessarily a fault of semver but it is what semver is selling.

            1. 2

              To clarify, when you say lots of people set things up this way, you’re not counting people who use lockfiles, correct?

              1. 2

                I guess not since I have experienced this fairly often.

              2. 1

                Why is this unfortunate? Due to lacking test between builds and shoddy upgrade procedures and builds that don’t lock down versions when the tests pass?

                Kinda like what @4ad said, it’s as if people expect this to solve world hunger when it should be regarded as a canned food in your pantry.

                1. 0

                  Assuming no locks, the biggest problem is that it means what you built and what I built are not guaranteed to be the same thing. So reproducible builds are out. It also means if you have a bug and I don’t, we don’t know why.

                  IME, semantic versioning has not helped me upgrade dependencies. I have to test the new update no matter what, despite the SemVer spec using words like MUST when it defines what things mean. And people fuck up their SemVers enough that backwards compatible changes end up not being backward compatible. So are we better off with SemVer than just some incrementing release number? I’m not really convinced the complexity of SemVer is really bringing a lot of value other than making us all feel like we elegantly solved a problem.

            2. 8

              Problem one: there is no such thing as an “internal change that fixes incorrect behavior” that is “backwards compatible”. If a library has a function f() in its public API, I could be relying on any observable behaviour of f() (potentially but pathologically including its running time or memory use, but here I’ll only consider return values or environment changes for given inputs)

              I don’t think that is much of a problem tbh. I wouldn’t consider it breaking backwards compatibility as long as the function still adheres to the API contract set forth by the documentation and general expectations. The function String() should return a string, the formatting of that string may change if it is not explicitly documented. The function ParseFile() will continue to parse the input file and return a value containing the parsed data. As long as the same set of input files is parsable according to the documentation there is no breakage in my view.

              However the limit of that would be if any behaviour ends up being widely used or was documented in a bad manner.

              What would be breaking backwards compatibility would be any change that would result in having to change documentation or function signatures. Those would require a more major release IMO.

              Of course, as long as majorVer=0 I will break backwards compatibility like the Hulk after being asked to write an essay on “smashing considered harmful” since it’s (IMO) not ready for production yet.

              1. 5

                Indeed, the “backwards compatible” in semver is with respect to the public API. This is not directly specified in the quoted part, but if you read the whole semver spec, I think it is pretty clear.

                So I would say, if you depend on observable behavior that is not part of the public API (including the documentation), then it is your own responsibility to have tests in place that ensure a new version exhibits that behavior.

                1. 0

                  Of course, as long as majorVer=0 I will break backwards compatibility like the Hulk after being asked to write an essay on “smashing considered harmful” since it’s (IMO) not ready for production yet.

                  I really hate this rule in semver. It seems designed to keep the major number small, people seem to have some lizard brain fear of large major versions, but it defeats the purpose of semver, IMO.

                  1. 3

                    Well, it’s a thing because majorVer=0, for me, implies the product is not ready for production. I’m still working out the kinks. I’m not going to make any promises.

                    Once I have everything set, I put down majorVer>=1, since I’m now more confident in the problem domain I also have less of a problem with upgrading major versions if it becomes necessary.

                    I don’t really fear large major versions, I tend to avoid it since most of the time you can bolt on the new functionality and provide shims for the old functions.

                    I don’t think it defeats the purpose of semVer as long as you eventually go majorVer=1.

                    1. 1

                      Well, it’s a thing because majorVer=0, for me, implies the product is not ready for production

                      I think the difference is versions and production are orthogonal to me. My workflow is deploying packages from branch builds until I feel it’s ready, so versionless.

                2. 2

                  I think this article contradicts itself, by saying that bug fixes are not backwards compatible, saying you should have a field for bug fixes, and that library authors should release a new library when they make backwards incompatible changes.

                  Am I missing something?

                  1. 1

                    The distinction between x and z is that z is about bug fixes, and x is about API changes, removing deprecated functionality, ground-up rewrites in swift/kotlin/elixir/whatever. You can easily see different semantics in that z is interpreted as “this still does the thing you think it does, but in a new way that we think is better” (I have to decide whether I agree with the maintainers) and x is interpreted as “this thing now does a possibly different thing in a certainly different way” (I have to decide whether that’s something I want, and whether that supplied by new-x is worth porting to).

                    1. 1

                      I would think that as you describe it, ground up rewrites could potentially be y. As for z changes, you’re right that they’re typically smaller than an x change, but that’s the original rationale for putting z last.

                      The changelog featured a nice discussion with Rhys Arkins (transcript included, search for semver) that made it clear to me that you have multiple competing concerns in versioning. As a library consumer, you care about the work a change will entail for you and the risk that it carries. Those two factors can come apart sharply, and none of semver’s values map clearly to either consideration.

                  2. 1

                    I somehow hadn’t considered the implications of Hyrum’s law on SemVer!

                    1. 1

                      I get the author’s point about the Z component being broken. If the library behaves incorrectly but the dependent program uses the incorrect behavior to get functionality, once the incorrect behavior is fixed in the library, the program will stop working. But the library will now be working correctly!

                      I think semver is not able to solve this issue, but it can mitigate against it: thorough testing and quality analysis before a 1.0.0 release is made is necessary, and careful review of anything that comes afterward.

                      1. 1

                        If strictly adhering to SemVer, wouldn’t the correct approach be to change the default behaviour, while still providing a fallback for the old incorrect behaviour? You could then provide a deprecation notice and actually remove the old incorrect behaviour with the next major version.

                        I think the problem is that libraries rarely do this (especially for “trivial” fixes) because it’s a PITA. But that’s not really SemVer’s fault.

                        1. 1

                          But that doesn’t solve the problem: dependents upgrade to Z+1 and their stuff breaks, which is expressly not what should happen when doing that under semver. Semver in this case tells you to bump the major version. I don’t mind, it works and it does satisfy the semver specification. I don’t have a problem with stupidly high major versions, since it’s all meaningless anyway, only the differentials are meaningful. Fundamentally going from 98 to 101 is the same as going from major version 3 to 6.

                          1. 1

                            Yeah, I think we’re on the same page. Either you figure out a way to fix the bug in a manner that’s backwards compatible, or you bump the major version. In practice people rarely do this for Z level fixes, but that’s more of a problem with how people interpret SemVer than with the philosophy itself.