A Proposal for Package Versioning in Go
<i>&gt; It is of course possible to build systems that use semantic versioning without semantic import versioning, but only by giving up either partial code upgrades or import uniqueness. Cargo allows partial code upgrades by giving up import uniqueness: a given import path can have different meanings in different parts of a large build. Dep ensures import uniqueness by giving up partial code upgrades: all packages involved in a large build must find a single agreed-upon version of a given dependency, raising the possibility that large programs will be unbuildable. ... Semantic import versioning lets us avoid the choice and keep both instead.</i><p>It&#x27;s a useful exercise to be critical of existing systems and see if there are opportunities to improve that they missed. That&#x27;s how progress happens.<p>At the same time, there is a common fallacy (especially among very smart people) that this might be an instance of. Even if it isn&#x27;t and the Go folks came up with something brilliant, I think it&#x27;s worth talking about because it occurs a lot elsewhere. I&#x27;ll call it the &quot;Missing Third Feature&quot;.<p>Cox&#x27;s claim boils down to. &quot;X gives you A but not B. Y gives you B but not A. I&#x27;ve just come up with Z which gives you both A and B, so it&#x27;s superior to both.&quot;<p>In many cases, though, the reality is. &quot;X gives you A <i>and C</i> but not B. Y gives you B <i>and C</i> but not A. I&#x27;ve just come up with Z which gives you A and B, <i>but not C</i> (because I&#x27;m likely not even aware that C exists).&quot;<p>I could be wrong, but if I had to guess, C is that vgo has no ability to express compatibility across multiple major versions.<p>Let&#x27;s say my package foo depends on bar. The maintainers of bar add a parameter to a function in it as well as fixing a number of other bugs. That signature change is a breaking change, so they rev foo to v2. My package bar does not call that function. In order to get the bug fixes, though, I have to change all of my imports to use foo&#x2F;v2. Anyone using bar must either fix all of their imports of foo to v2, or end up with two versions of foo in their application (which might cause weird behavior if values from one foo end up flowing to another).<p>The key problem is that from a package&#x27;s own perspective, &quot;breaking change&quot; is defined conservatively — any change that <i>could possibly</i> break even a single user is a breaking change and necessitates a major version bump. From a package <i>consumer&#x27;s</i> perspective, many nominally breaking changes do not actually break that particular consumer. Package managers like Cargo let you express that gracefully. My package foo can say, &quot;I depend on bar and work with bar &gt;1.2.3 &lt;3.0.0&quot; if I know that foo isn&#x27;t impacted by any of the potential breakages in bar 2.0.0 or 3.0.0. That in turn lets my package be used in a greater number of version contexts without causing undo pain to <i>my</i> consumers.<p>This may not turn out to be a big deal. It&#x27;s hard to tell. But my general inclination when I&#x27;m designing a system is that if my idea seems unilaterally better than the competition in all axes, then I strongly suspect those systems have a feature that I am oblivious to and that mine lacks and I try to figure out what it is they know that I don&#x27;t.