Two years have passed since the RSC designed and integrated the Go mod into the Go language, and Gopher is still plagued by go Mods.

This article lists some of the problems that my colleagues and I have encountered with the Go mod. Some of the problems are with the Go mod itself, and some may be with the third-party GoProxy implementation.

If you’ve ever worked on a large GO project, there are a few that will make you smile.

Side effects of the Go command

It was hard for Gopher to understand why every go FMT file was so stuck after the new version was upgraded.

Many of go’s subcommands have side effects since the go mod was introduced, such as Go Test, go FMT, Go Build, and Go List.

For example, go FMT above, I just want to format my file, not to download the dependency, but still have to wait patiently for the dependency to download.

The go.mod file is even more confusing: Why go mod keeps changing with go test, go.mod is modified after go test.

This is why go.mod and go.sum always appear in our file changelist. These two files are especially likely to conflict during large projects.

Go. Sum Git merge conflict

When many colleagues work in the same Git repository, there is no way to auto merge code, even though we have divided our responsibilities:

You’ve seen a lot of merge conflicts like the one above with go.sum lying below.

Semver specification

The design of the Go mod assumes that the community strictly adheres to semver’s specifications:

Given a version number MAJOR.MINOR.PATCH, increment the:

MAJOR version when you make incompatible API changes,

MINOR version when you add functionality in a backwards compatible manner, and

PATCH version when you make backwards compatible bug fixes.

Minor upgrades such as 1.7.4 -> 1.7.5 should not introduce incompatible upgrades, but it is clear that Google is overestimating the integrity of the open source community. Many open source library authors tend to modify their apis casually.

Even Google’s own GRPC-Go project has done incompatibilities with minor updates: Update your SemVer Policy Please – Breaking changes in minor versions causing extincting.

Moreover, the authors of GRPC-Go are fair enough to admit that they still allow some incompatible exceptions to semver.

There have even been behavior changes that were not noticeable from the release Notes that have led to bugs in the production environment for helm projects that rely on GRPC-GO, which have been infuriating.

Well done, Google engineers.

In addition to the human problem, there is an exception to the Semver specification:

Major version zero (0.y.z) is for initial development. Anything MAY change at any time. The public API SHOULD NOT be considered stable.

Go mod design did not consider this situation, MVS algorithm in the scope of 0. Y.z will try to upgrade the small version of the same situation, making people complain a lot, unbearable.

In the cloud native space that has exploded in the past two years, there are many projects that stay in version 0.x.y for two or three years. It’s perfectly normal for practitioners to rely on a version number of 0.x. If you ask go mod replace who uses it the best? That must be cloud native developers.

Version information diffusion

Due to the design of the Go mod, if a dependent library is updated with a new version, our import path will change:

The CHI project has been upgraded to V5. All code imported into the Chi lib needs to be imported. Happy or not. We have to upgrade to new API compatibility and change all these import paths that are scattered around.

This is by no means good design.

Implementations of GoProxy vary

Because of special reasons, domestic gopher basically needs to configure domestic companies/personal development of goProxy to speed up the dependent download, these proxies do not use the same code, so the implementation details often differ.

For example, when a library does not exist, some GoProxy returns 404, while some goProxy returns 500(this is a real case when I use a goProxy).

Let’s take a look at some more surprising examples to help you understand the strangeness.

Delete library to run road

As a simple experiment, follow these steps:

  1. Create repository A on Github
  2. Use goProxy X to go build
  3. Delete warehouse A
  4. Delete mod cache and run go build using goproxy X/Y/Z respectively
First go build Delete library goproxy. Cn Delete library goproxy. IO Tencent GoProxy after library deletion Aliyun GoProxy after library deletion
goproxy.cn Can build Do not build Do not build Do not build
goproxy.io Can build Can build Do not build Do not build
Tencent goproxy Can build Do not build Can build Do not build
aliyun goproxy Can build Do not build Do not build Can build

This time, I selected the four most widely used GoProxy in China. I used one of them to cache external dependencies once, and it can still be built after deleting the library. However, if you have not relied on the goProxy cache before, only goproxy.cn can still download the dependencies normally.

After consulting the original author, goproxy.cn has not found a dependency, but gosumDB has a value, it will go to the official index.golang.org for search, and gosumDB has a value, This will normally be cached in proxy.golang.org (even if you set up a third-party goproxy). At this time, goproxy.cn will also pull from the official GoProxy, so the user’s build can be successful.

A project without a vendor would theoretically result in schrodinger’s build because Gopher used a different GOPROXY.

If we take a closer look at sum.golang.org, the official cache expiration description for external libraries is also somewhat vague.

Fuzzy storage life

proxy.golang.org does not save all modules forever. There are a number of reasons for this, but one reason is if proxy.golang.org is not able to detect a suitable license. In this case, only a temporarily cached copy of the module will be made available, and may become unavailable if it is removed from the original source and becomes outdated. The checksums will still remain in the checksum database regardless of whether or not they have become unavailable in the mirror.

In proxy.golang.org, the repository is not permanent in GoProxy, at least not in proxy.golang.org. The number of reasons given is vague.

There’s no way we can bet our jobs on this kind of ethereal rhetoric, except to assume that GoProxy won’t cache our warehouses permanently. There is no way to expect our dependence to last forever. After the original warehouse died from Github, sooner or later, it will die on various goProxy.

Even two years after the launch of the Go mod, it was still necessary for us to keep dependencies in vendor.

The left Pad tragedy in the JS community many years ago may not have taught current software designers much: how one Programmer broke the Internet, have we forgotten how to program.