Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ


Choose your language

InfoQ Homepage News Keeping Go "Boring" in Go 1.21: How Google Grants Backward Compatibility

Keeping Go "Boring" in Go 1.21: How Google Grants Backward Compatibility

In a recent article, Google engineer Russ Cox detailed what Google does to make sure each new Go release honors Go's backward-compatibility guarantee. This includes generalizing GODEBUG in Go 1.21 to cover even subtle incompatibility cases.

Introduced in Go 1, Go's backward compatibility guarantee ensures all correct Go programs will continue to work with future releases of the language. As Cox explains, this goal entails two major efforts: checking that each API change does not break anything and extensive testing to catch subtler incompatibility cases.

While the idea of checking API changes to prevent any breaking change from entering a new release may sound obvious, Cox also describes a number of cases where testing is the way to go:

The most effective way to find unexpected incompatibilities is to run existing tests against the development version of the next Go release. We test the development version of Go against all of Google’s internal Go code on a rolling basis. When tests are passing, we install that commit as Google’s production Go toolchain.

The examples provided by Cox fall into a few distinct categories, including changing the precision of the time.Now() library function, output or input changes, and protocol changes. There are cases, though, where a breaking change is brought by an important new features that must make it into the language, such as HTTP/2 support in Go 1.6 or SHA1 deprecation in Go 1.18.

To handle this kind of cases, Go provided a specific feature in release 1.6, the GODEBUG environment variable, which could be used to disable HTTP/2 for specific modules. In Go 1.21, Cox says, the GODEBUG mechanism has been extended and formalized.

Instead of using an environment variable, Go 1.21 uses a go:debug setting that can be specified in a package's main. This works in combination with the Go version listed in a module's go.mod file, so that each given Go version will have a default behavior that ensures compatibility on a feature-by-feature basis, while go:debug may be used to override it. For example, given that Go 1.21 changes panic(nil) behavior, developers may want to upgrade their toolchain to Go 1.21 but keep the old behavior. This is possible by overriding the version-specific go:debug setting by using:

//go:debug panicnil=1 

According to Cox, this lets every new version of Go be the best implementation of older versions, fixing bugs while preserving backward-compatibility even in the face of important breaking changes.

Cox's article spun quite an interesting conversation on Hacker News, mostly on the relative merit of granting backwards compatibility in a language, versus introducing a new non-backward compatible version, as it happened in Python when going from Python 2 to Python 3; and comparing the approach taken by Go versus those taken by Java, C++, and .NT languages.

About the Author

Rate this Article