The Go module not only follows the semantic version specification 2.0.0, but also goes a step further by giving deeper meaning to the major in the semantic version.

  • X.X: For the case where the major version number (major) is 0, it implies that your current API is still in an unstable state, and the new minor version may not be backward compatible.
  • X.X: the current API is in a stable state, the increase of minor only means the increase of new feature, the API is still backward compatible
  • X.X: the addition of major means that the API is no longer backward compatible

Question: Do you know which version numbers in the go module imply that the current API is unstable?

But what sets go module apart from the rest is that once your major is greater than or equal to 2, your module path must be suffixed with v2 (if tag is v3.X.X, then it is suffixed with v3, and so on).

Also, add v2 to the package reference path, e.g. go.etcd.io/etcd/client/v3.

This is an odd way to write it, equivalent to putting a salve on the normal easy-to-understand module path to indicate which version of the library is being introduced?

Why add this v2, v2 suffix, there must be some considerations

Most notably, the developer of Go (in this case Russ Cox) pointed out in the import compatibility rule:

If an old package and a new package have the same import path, the new package must be backwards compatible with the old package.

This idea is a good one. For example, you can use multiple versions of the same library in your project, with v1 versions handling previous legacy logic, v2 versions handling new logic, and v3 versions experimenting with future versions. Different versions of the same set of libraries can coexist and there is no place for version conflicts.

And when programmers see these module paths, it is clear that the versions are not compatible and who is the newer version.

But this way is also very controversial, in practice also brings a lot of problems, I am in the development of rpcx suffer from it, and etcd, for example, you can see its v3.4.X version, is because there is no v3 suffix, resulting in go command to download or import (get) these package when it simply can not be downloaded.

The vX suffix contaminates the package path

Originally, the normal package path is generally the repository path + package name, or go module under module path + package, but once the version is greater than or equal to 2, you have to add a suffix v2, v3, etc., the meaning of package path has changed.

Of course, we can tolerate it, the big deal is to close our eyes and use it, but the most painful thing is that many Go beginners do not understand this setting, do not know to import the new library version to add the v2 suffix, a face of confusion.

v0, v1 and v2 data types are not compatible

After adding v2, v3 and other suffixes to module path, it is assumed that these package are different package, although most of their data types are not changed, or backward compatible, and cannot be assigned directly, but still need to be strongly transferred.

For example, if your project depends on Auth 1.0.0 and also depends on Auth 2.0.0, then even if A.Config doesn’t change in both versions, you can’t assign Auth.Config to Auth/v2.Config, but you need to add the logic of strong transfer in the code, so that the two are transferred to each other. Once v3 is released, it’s a long switch branch to deal with this situation, and if v4 is released, the logic is even more complicated.

Impose a significant burden on third-party library developers

Although you think I also release v2, v3, v4 and several other versions, the version route is very clear and not complicated to manage, no big deal.

However, if your library is a very popular library and many developers have developed third-party libraries based on your library, it can be very painful

This means that once you release a new version, these third-party developers have to update their libraries in time to release their new v2, v3 versions based on your new version. This is like a virus that initially expands. It’s a big burden to the developers.

Of course, it is a matter of opinion, these situations may not be encountered by you, or will not bring you trouble, so it is not a problem. I, on the other hand, was deeply hurt by v2 when I was developing rpcx or answering some users’ questions, and my little mind couldn’t bear the weight of v2.

Some open source projects, in order to avoid the version number jump to v2, using some other methods, such as protobuf-go, is doing a new version of the refactoring, the changes are very large, not compatible with the previous version, can be the previous version are v1.X.X, then how to do? Change the module path name.

  • github.com/golang/protobuf: support for the previous protobuf go, currently up to version v1.5.2
  • google.golang.org/protobuf: new version of module path, current highest version v1.27.0, initial version v1.20.0

For my rpcx project, because the version number was released to v6.X.X before the go module came out. I want to go back to the old days, but it seems that I can’t go back. So I took an extreme approach and rebuilt tag with all the version numbers defined in v1.X.X. Fortunately, it affected fewer users, so no users complained.

My approach is rather extreme, but the reason I didn’t cause users to complain is that I always insist on the coexistence of go module and GOPATH. The vast majority of users use the master branch, or fork a new version themselves, so the impact is minimal.


Reference https://colobu.com/2021/06/28/dive-into-go-module-2/