Wandering Thoughts archives


Understanding '+incompatible' in Go module version names

As we all know, when Go code uses modules, you specify a version of the module, and the Go tooling will record its take on this version in places like go.mod. The exact details are covered in the "Versions" section of the documentation and the definition of canonical version. Since Go modules came in, I've seen a number of modules with a (canonical) version that included '+incompatible', and encountered Go generating them itself when I tried to experimentally modularize a non-modular third party Go program by hand. However, for a long time I didn't really understand what it meant and what you could do with this. The short version is that it's there to deal with a corner case for pre-modular packages.

To start with, there are a few ways to have properly set up Go modules (or at least a few common ways). If you have an improperly set up Go module, such as one where the version tags and the go.mod disagree with each other about what the version is (eg if your 2.0.0 version has a go.mod that claims the module is 'example.org/cks/mymod' instead of 'example.org/cks/mymod/v2'), I believe that the Go tools will just throw errors. And generally if you're using go.mod, you need to get it right.

But there is one 'improper' case that you can get into with a non-modularized Go package, and that is if the package has a version tag with a major version above 1 (with no go.mod, since it's a package instead of a module). In the pre-modular Go world, this was perfectly valid thing to do, although not necessarily friendly to your users (since 'example.org/cks/mymod' could silently move major versions on them, presumably with API incompatibility). This isn't just theoretical and there were a variety of Go packages that did this, such as github.com/google/go-github (which got up to v17 before it modularized).

In the world of Go modules, the +incompatible suffix to a version is how Go still allows you to specify and use such a package in your go.mod. You can only use +incompatible with packages, not modules; the moment a repository adds a go.mod in some version, you stop using +incompatible from that version onward. To make this concrete, if you do 'go get github.com/google/go-github@latest', you will get the version v17.0.0+incompatible, although as I write this v48.0.0 is the latest version. The reason Go stops at 'v17.0.0' is that that was the last version before go-github modularized in v18.0.0.

As you might expect, +incompatible can appear on pseudo-versions, if you want to set a (minimum) version in your go.mod to some commit instead of just a version tag.

As covered in the "+incompatible versions" section of the documentation, the major version number in the version is basically decorative. If you ask Go to update the version of the package, for example, it will feel free to upgrade you from 'v16.0.0' to 'v17.0.0'. You could phrase this as that all versions of a package have the same major version as Go tools see it, or you could say instead that Go is mirroring the pre-modular behavior of 'go get -u', where if you upgraded you always got the latest (VCS) version of the package and sorting out the actual package versions and any API changes were your problem.

(In general the "+incompatible versions" section of the documentation isn't very long and is worth reading for the technical details, although you'll wind up going through various cross references to the rest of the Go Modules Reference.)

This means that you can't use +incompatible as part of modularizing a program to perfectly duplicate what you would have gotten with a non modular build in Go 1.17 and earlier, as I've discovered before. In fact, not even 'go install <package>/cmd/something@latest' will do this, which means that in Go 1.18 and later it may be impossible to properly build and install some old, non-modularized Go programs. I haven't run into this yet, though, and I suspect it would be relatively uncommon.

(It would require a package to modularize, change major version, and change its API, and then a non-modularized program to be updated to use the new API. At that point, the +incompatible version of the modularized package has an API that's not compatible with the program.)

programming/GoModulePlusIncompatibleMeaning written at 21:50:55; Add Comment

Page tools: See As Normal.
Login: Password:
Atom Syndication: Recent Pages, Recent Comments.

This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.