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
) is0
, it implies that your currentAPI
is still in an unstable state, and the new minor version may not be backward compatible.X.X
: the currentAPI
is in a stable state, the increase ofminor
only means the increase of newfeature
, theAPI
is still backward compatible- X.X: the addition of
major
means that theAPI
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 previousprotobuf go
, currently up to versionv1.5.2
google.golang.org/protobuf
: new version ofmodule path
, current highest versionv1.27.0
, initial versionv1.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/