Not IBM’s Multiple Virtual Storage, minimum version selection.
In Russ Cox’s article on minimum version selection, he addresses the problem of what to do with a conflict, when two dependencies of your program ask for two versions of a common dependency of theirs, one newer, one older.
In his model, you can only have one, so one must choose in a manageable way, which to use.
David J Brown’s approach
In the model used by Multics, Solaris and Linux glibc, what are now semantic versions were interpreted strictly, but the interfaces were versioned, not the entire library. If I built a program that used interface X and Y, part of a library that was at version 1.42, I would always link with X@1.42.Z and Y.1.42.Z, for some constantly increasing Z.
Z indicated a bug fix. Version 1.42 indicated a version with just X and Y, while 1.45 indicated a compatible change had happened, such as the addition of interface W. If I changed my program to use W, I would have to use at least version 1.43.Z, to get W added. In effect, I wouldn’t need to change my program until I wanted to.
Of course, I often wanted to, but rarely on the day W was added. Usually a fair bit later. Occasionally never, but that was OK. On Solaris, we had a compatibility guarantee: if you ran on Solaris 2.5 and only used legal interfaces, you ran on 2.6.
The whole idea was to support controlled evolution, without forcing a revolution. A short description of avoiding revolutions and other evil problems is in “DLL Hell”, and avoiding an NP-complete problem.
Why choose to upgrade?
- to have all the bug-fixes, something we called “adoption”
- to add or change an interface, called evolution.
In our model, both 1.42 and 1.43 got bug fixes. If you look at the semantic versioning spec, it supports the same thing: backwards-compatible bug fixes just increment the third number in the triple. The only difference is that X@1.42.14 and X@1.43.2 can both exist. This has been the case in our model since Multics, where the problem was first encountered.
If Go source libraries did versioning the way Multics/Solaris/Linus shared libraries did, then so long as I didn’t add new interfaces, I could keep building my program with newer and newer versions of my libraries with no change.
That’s a non-trivial advantage: if I’m the only maintainer of a filter program like cmp(1) that’s stable, the only upgrades I really want are security bug-fixes.
The second reason is to choose when to change, not be forced into it.
In the example from the “DLL Hell” paper, we wanted to add a new country (Canada) to an existing program that worked fine in the US. Only the Canadian versions needed to change now: the US versions could wait until their owners were ready to upgrade, often after waiting to see if there were any bugs.
The alternative is to have a “flag day” thrust upon you. We hated flag days, almost as much as Linus does. Having to stop all development and dedicate everyone to updating their libraries is undesirable. If you have more that half a dozen customers, it’s insupportable.
In the extreme case, if you have to port all your programs from Linux 2.5 to 2.6, that’s a really good reason to port to BSD instead. You can easily understand Linus’ position.
Applied to MVS
Given that you’ve chosen to upgrade, there are two cases:
- I’m building a new program and I want the newest, shiniest version of everything. OK, not a true upgrade, but time for a decision, anyway.
- I’m updating an old program, and I actively want to call W or a newer-but-incompatible version of X.
In either case, the differences should be explicit and under your control.
With MVS and/or gps, the problem is the same, just a harder in that you have to stop when you find a conflict and change the whole library. In the common case, it’s an annoyance.
In the extreme case, it can be NP-complete, and you have a rewriting task ahead of you.
I really, really think one should avoid NP-complete problems: I’m pretty sure David J had the right idea.