Banzai Cloud is now part of Cisco

Banzai Cloud Logo Close
Home Products Benefits Blog Company Contact

Our favorite Go 1.13 features

Author Márk Sági-Kazár

The content of this page hasn't been updated for years and might refer to discontinued products and projects.

At Banzai Cloud we work with cutting edge cloud technology every single day. Nowadays cloud and container related softwares are almost exclusively written in the Go programming language, so it’s no surprise most of our projects (Pipeline or Bank-Vaults to name a few) are also written in Go.

In this post we will take a look at our favorite features of the latest (1.13) Go release.

Reproducible builds — -trimpath 🔗︎

Reproducible builds (or deterministic compilation) is the process of compiling a software in a way that ensures that the resulting artifact can be reproduced, given certain conditions are met. In practice it means that given the same input, two separate environments should compile the exact same binary, bit by bit. The primary reason for that is usually trust: signed source code compiled deterministically can prove that a binary was built from that source code. Even if that trait is not used, it’s nice to have: no one knows when it could come in handy.

As of Go 1.13 the go build command accepts a new flag -trimpath which instructs the compiler to remove all file system paths from the compiled executable. As a result, you will see module paths in stack traces instead of absolute paths of the build environment.

Building multiple binaries 🔗︎

Before Go 1.13, if you tried to build a project with multiple binaries using the following command, you received an error:

go build -o build/ ./cmd/...
go build: cannot use -o with multiple packages

As of the latest release though, the above command successfully builds all binaries in cmd/, given the parameter given to the -o option is a directory. If it’s not, the command will exit with the following error:

go build -o non_existing ./cmd/...
go build: cannot write multiple packages to non-directory non_existing

It’s worth noting that this feature does not seem to speed up the overall compilation time at all, so it only spares the extra commands, but not time.

Go modules improvements 🔗︎

Go modules received a lot of improvements in the latest release. Although GO111MODULE still defaults to auto, the auto setting now activates the module mode whenever the current working directory (or bellow) contains a go.mod file, even inside GOPATH. This essentially means no more export GO111MODULE=on if you prefer keeping your code in a GOPATH.

Go 1.13 is a big milestone in the Go modules “experiment”: it comes with a default GOPROXY enabled. Although this setting was available in Go 1.11 as well, there were no “official” proxies available. There was the Athens Project for those who wanted to play with proxies, but we had to wait for an official one until August.

Go 1.13 comes with the following default setting: GOPROXY=https://proxy.golang.org,direct which means “use the central proxy for everything you can and fallback to source”. Enabling GOPROXY meant a 30% improvement in our build time, but that’s not the only upside of the proxy protocol: it’s a good way for enterprises to setup a secure gateway for downloading dependencies from the internet.

Another improvement in Go 1.13 is support for disabling proxies for certain module paths by setting the GOPRIVATE environment variable. This is very useful for private packages that are likely to be unavailable through proxies (e.g. private on GitHub or only available on internal network).

For example, our internal software packages are built with the following setting: GOPRIVATE=github.com/banzaicloud/*.

Nullable time 🔗︎

It’s a long requested feature to support nullable time.Time values in the sql package. A typical example would be an updated or a deleted timestamp of any resource stored in a database.

As of version 1.13, Go has an sql.NullTime type.

Better errors 🔗︎

Go is heavily criticized for its error handling strategies. At best, we can call it unconventional. The community expressed it’s pain over and over again and for a long time nothing happened, which led to userland solutions and practices. We already wrote about these in the past:

Since the Go 2 proposals though, the core team actively works on error handling in Go. Luckily, the first few features are available in Go 1.13. These features are mostly related to error wrapping, so the largest pain points are still unresolved, but it’s a great first step.

The three major additions are three new functions:

  • Unwrap checks if an error wraps another one and returns it
  • As unwraps an error sequentially and looks for one in the chain that can be assigned to another
  • Is unwraps an error sequentially and looks for one in the chain that matches another

Unfortunately (or maybe not?) the implemented solutions totally disregarded existing community standards (eg. github.com/pkg/errors package), so until existing packages are updated these new features might not work for you.

This is why we decided to create a drop-in replacement for the stdlib errors and github.com/pkg/errors packages: emperror.dev/errors. You can simply replace all your errors and github.com/pkg/errors package imports with this package, and you will automatically receive best of both worlds (and more):

  • Go 1.13 error wrapping
  • Stack traces
  • Message annotations
  • Arbitrary key-value pairs

Conclusion 🔗︎

Although Go 1.13 does not seem to contain much at first, it comes with a lot of useful features and improvements that have real effect on development, so this might really be the best Go release ever.

What’s your favorite feature? Let us know in the comments!

Further reading 🔗︎

  • https://golang.org/doc/go1.13
  • https://golang.org/cmd/go/#hdr-Module_configuration_for_non_public_modules
  • https://blog.golang.org/module-mirror-launch
  • https://go.googlesource.com/proposal/+/master/design/go2draft.md