This article is a translated article: blog.golang.org/v2-go-modul… Translated by Jean de Klerk and Tyler Bui-Palsu


Introduction to the

This article is part four of the Go Modules system

  • Part 1: Use Go Modules translation
  • Part 2: Migrate to Go Modules Translation
  • Part 3: Release Go Modules translation
  • Part 4: Go Modules: v2 and later (this article)

As successful projects mature and new requirements are added, early functional and design decisions may no longer apply. Developers may want to incorporate their lessons by removing deprecated features, renaming types, or breaking complex programs into manageable pieces. This type of change requires downstream users to make changes to migrate their code to the new API, so it should not be made without careful consideration of the benefit-cost ratio.

For projects that are still in the early stages of development (the major release number is V0), users will expect occasional major changes. For projects that claim to be stable (major release V1 or higher), major changes must be made in the new major release. This article explores major version semantics, how to create and publish new major versions, and how to maintain multiple major versions of a Go Modules.

Major version and module path

The module establishes an important principle in Go, namely “Import compatibility rules”

If the import path of the old package and the new package is the same, the new package must be backward compatible with the old package

According to this principle, a new major version of a software package is not backward compatible with previous versions. This means that the new major version of the package must use a different module path than the previous version. Starting with v2, the major version number must appear at the end of the module path (declared in the module statement of the go.mod file). For example, when the module github.com/googleapis/gax-go developers to develop the v2, they used a new module path github.com/googleapis/gax-go/v2. Want to use the v2 user must change their package import and module for github.com/googleapis/gax-go/v2

The need for a major version number suffix is one of the ways that the Go module differs from most other dependency management systems. Suffixes are used to solve the problem of diamond dependence. Before the Go module, Gopkg. in allowed package maintainers to follow what we now call import compatibility rules. With gopkg.in, if you rely on one package that imports gopkg.in/yaml.v1 and another package that imports gopkg.in/yaml.v2, there is no conflict. Because the two YAML packages have different import paths (they use a similar version suffix to Go Modules). Since gopkg.in and Go Modules share the same version number suffix method, the Go command accepts.v2 in gopkg.in/yaml.v2 as a valid version number. This is a special case where modules hosted in other domains need to use a slash suffix like /v2 for compatibility with gopkg.in.

Major Release policy

The recommended strategy is to develop v2+ modules in directories named after the main release suffix.

Github.com/googleapis/gax-go @ master branch/go mod - module github.com/googleapis/gax-go/v2 / go mod - > module github.com/googleapis/gax-go/v2Copy the code

This approach is compatible with tools that don’t support Go Modules: the file path in the repository matches the path expected by the Go Get command in GOPATH mode. This strategy also allows all major releases to be developed together in different directories.

Another strategy might be to place the main version on a separate branch. However, if the source code for V2 + is on the default branch of the repository (generally master), tools that do not support versions (including the Go command in GOPATH mode) may not be able to distinguish between major versions.

The examples in this article follow the main edition subdirectory policy, so they provide maximum compatibility. We recommend that authors of modules follow this policy as long as they have users developing using the GOPATH pattern.

Release V2 and later versions

This article take github.com/googleapis/gax-go:

$ pwd/tmp/gax-go $ ls CODE_OF_CONDUCT.md call_option.go internal CONTRIBUTING.md gax.go invoke.go LICENSE go.mod tools.go README. Md go. Sum RELEASING. Md header. Go $cat go. Mod module github.com/googleapis/gax-go go require (1.9 Github.com/golang/protobuf v1.3.1 golang.org/x/exp v0.0.0-20190221220918-438050 ddec5e golang.org/x/lint V0.0.0-20181026193005-c67002cb31c3 golang.org/x/tools v0.0.0-20190114222345-BF090417da8b google.golang.org/grpc v1.19.0 Honnef. Co/go/tools v0.0.0-20190102054323 - c2f93a96b099) $Copy the code

To start developing github.com/googleapis/gax-go v2 version, we will create a new v2 / directory and copies the contents of the package to the directory.

$ mkdir v2
$ cp *.go v2/
building file list ... doneGo gax.go header.go invoke.go tools.go sent 10588 bytes Received 130 bytes 21436.00 bytes/ SEC Total size is 10208 Speedup is 0.95 $Copy the code

Now we create a go.mod file belonging to v2 by copying the current go.mod file and adding the /v2 suffix to the module path.

$ cp go.mod v2/go.mod
$ go mod edit -module github.com/googleapis/gax-go/v2 v2/go.mod
$
Copy the code

Note: Version V2 is treated as a separate module from version V0 / v1, and both can coexist in the same build. Therefore, if your V2 + module has multiple packages, you should update them to use the new /v2 import path, otherwise, your V2 + module will depend on your V0 / v1 module. To upgrade all github.com/my/project to github.com/my/project/v2, use the find and sed commands:

$ find . -type f \
    -name '*.go' \
    -exec sed -i -e 's,github.com/my/project,github.com/my/project/v2,g' {} \;
$
Copy the code

We now have a V2 module, but we need to experiment and modify it before the release. Before we release V2.0.0 (or any other version without a pre-release suffix), we can develop and make significant changes, just as we decide to implement a new API. If we want users to experiment with the new API before it is officially released, we can choose to release the v2 pre-release version:

$git tag v2.0.0-alpha.1 $git push origin v2.0.0-alpha.1 $Copy the code

Once we are satisfied with the V2 API and are sure that there will be no other major changes, we can make the Git tag v2.0.0.

Git tag v2.0.0 $git push origin v2.0.0 $Copy the code

At that point, there are two major releases to maintain. Backward compatible changes and bug fixes are released with new sub-releases or patch releases (e.g. V1.1.0, v2.0.1, etc.).

conclusion

Major release changes incur development and maintenance costs and require additional downstream user costs to migrate. The larger the project, the greater the cost of this major release change. Major release changes should be made only after a compelling reason has been established. Once a compelling reason for a major change has been identified, we recommend multiple major releases in the Master branch because it is compatible with a variety of existing tools.

Major changes to the V1 + module should always occur in the new vN+1 module. When a new module is released, it means more work for maintainers and users who need to migrate to the new package. Therefore, maintainers should validate their apis before releasing stable versions and carefully consider whether major changes are really necessary after v1.

Related articles

  • Release the Go Modules
  • Enable module mirroring and database verification
  • Migrate to Go Modules
  • Using the Go Modules
  • Go Modules for 2019
  • Recommendations for software package versioning in Go
  • Cover story
  • App Engine SDK and Workspaces
  • Organize the Go code