The Go Blog

Announcing the 2019 Go Developer Survey

Todd Kulesza
20 November 2019

Help shape the future of Go

Since 2016, thousands of Gophers around the world have helped the Go project by sharing your thoughts via our annual Go Developer Survey. Your feedback has played an enormous role in driving changes to our language, ecosystem, and community, including the gopls language server, new error-handling mechanics, the module mirror, and so much more from the latest Go 1.13 release. And of course, we publicly share each year's results, so we can all benefit from the community's insights.

Today we are launching the 2019 Go Developer Survey. We'd love to hear from everyone who uses Go, used to use Go, or is interested in using Go, to help ensure the language, community, and ecosystem fit the needs of the people closest to it. Please help us shape Go's future by participating in this 15-minute survey by December 15th: Take the 2019 Go Developer Survey.

Spread the word!

We need as many Gophers as possible to participate in this survey to help us better understand our global user base. We'd be grateful if you would spread the word by sharing this post on your social network feeds, around the office, at meet-ups, and in other communities. Thank you! a new hub for Go developers

Steve Francia and Julie Qiu
13 November 2019

Over the last two years, as we’ve spoken with users at companies of all sizes, we’ve heard three questions repeatedly: who else is using Go, what do they use it for, and how can I find useful Go packages?

Today we are launching, a new hub for Go developers, to help answer those questions. There you will find a wealth of learning resources to get started with the language, featured use cases, and case studies of companies using Go.

(Note that is still the home for the open source Go project and the Go distribution. is a companion site to provide these supporting resources.)

Clicking on Explore brings you to, a central source of information about Go packages and modules. Like, serves Go documentation. However, it also understands modules and has information about all versions of a package, including all releases of the standard library! And it detects and displays licenses and has a better search algorithm. You can follow Go issue 33654 for future developments.

Today’s launch is our minimum viable product for, so we can share what we’ve built to help the community and get feedback. We intend to expand the site over time. If you have any ideas, suggestions or issues, please let us know via the “Share Feedback” and “Report an Issue” links at the bottom of every page. Or you can send your bugs, ideas, feature requests, and questions to

Go Turns 10

Russ Cox, for the Go team
8 November 2019

Happy birthday, Go!

This weekend we celebrate the 10th anniversary of the Go release, marking the 10th birthday of Go as an open-source programming language and ecosystem for building modern networked software.

To mark the occasion, Renee French, the creator of the Go gopher, painted this delightful scene:

Celebrating 10 years of Go makes me think back to early November 2009, when we were getting ready to share Go with the world. We didn’t know what kind of reaction to expect, whether anyone would care about this little language. I hoped that even if no one ended up using Go, we would at least have drawn attention to some good ideas, especially Go’s approach to concurrency and interfaces, that could influence follow-on languages.

Once it became clear that people were excited about Go, I looked at the history of popular languages like C, C++, Perl, Python, and Ruby, examining how long each took to gain widespread adoption. For example, Perl seemed to me to have appeared fully-formed in the mid-to-late 1990s, with CGI scripts and the web, but it was first released in 1987. This pattern repeated for almost every language I looked at: it seems to take roughly a decade of quiet, steady improvement and dissemination before a new language really takes off.

I wondered: where would Go be after a decade?

Today, we can answer that question: Go is everywhere, used by at least a million developers worldwide.

Go’s original target was networked system infrastructure, what we now call cloud software. Every major cloud provider today uses core cloud infrastructure written in Go, such as Docker, Etcd, Istio, Kubernetes, Prometheus, and Terraform; the majority of the Cloud Native Computing Foundation’s projects are written in Go. Countless companies are using Go to move their own work to the cloud as well, from startups building from scratch to enterprises modernizing their software stack. Go has also found adoption well beyond its original cloud target, with uses ranging from controlling tiny embedded systems with GoBot and TinyGo to detecting cancer with massive big data analysis and machine learning at GRAIL, and everything in between.

All this is to say that Go has succeeded beyond our wildest dreams. And Go’s success isn’t just about the language. It’s about the language, the ecosystem, and especially the community working together.

In 2009, the language was a good idea with a working sketch of an implementation. The go command did not exist: we ran commands like 6g to compile and 6l to link binaries, automated with makefiles. We typed semicolons at the ends of statements. The entire program stopped during garbage collection, which then struggled to make good use of two cores. Go ran only on Linux and Mac, on 32- and 64-bit x86 and 32-bit ARM.

Over the last decade, with the help of Go developers all over the world, we have evolved this idea and sketch into a productive language with fantastic tooling, a production-quality implementation, a state-of-the-art garbage collector, and ports to 12 operating systems and 10 architectures.

Any programming language needs the support of a thriving ecosystem. The open source release was the seed for that ecosystem, but since then, many people have contributed their time and talent to fill the Go ecosystem with great tutorials, books, courses, blog posts, podcasts, tools, integrations, and of course reusable Go packages importable with go get. Go could never have succeeded without the support of this ecosystem.

Of course, the ecosystem needs the support of a thriving community. In 2019 there are dozens of Go conferences all over the world, along with over 150 Go meetup groups with over 90,000 members. GoBridge and Women Who Go help bring new voices into the Go community, through mentoring, training, and conference scholarships. This year alone, they have taught hundreds of people from traditionally underrepresented groups at workshops where community members teach and mentor those new to Go.

There are over a million Go developers worldwide, and companies all over the globe are looking to hire more. In fact, people often tell us that learning Go helped them get their first jobs in the tech industry. In the end, what we’re most proud of about Go is not a well-designed feature or a clever bit of code but the positive impact Go has had in so many people’s lives. We aimed to create a language that would help us be better developers, and we are thrilled that Go has helped so many others.

As #GoTurns10, I hope everyone will take a moment to celebrate the Go community and all we have achieved. On behalf of the entire Go team at Google, thank you to everyone who has joined us over the past decade. Let’s make the next one even more incredible!

Go Modules: v2 and Beyond

Jean de Klerk and Tyler Bui-Palsulich
7 November 2019


This post is part 4 in a series.

As a successful project matures and new requirements are added, past features and design decisions might stop making sense. Developers may want to integrate lessons they've learned by removing deprecated functions, renaming types, or splitting complicated packages into manageable pieces. These kinds of changes require effort by downstream users to migrate their code to the new API, so they should not be made without careful consideration that the benefits outweigh the costs.

For projects that are still experimental — at major version v0 — occasional breaking changes are expected by users. For projects which are declared stable — at major version v1 or higher — breaking changes must be done in a new major version. This post explores major version semantics, how to create and publish a new major version, and how to maintain multiple major versions of a module.

Major versions and module paths

Modules formalized an important principle in Go, 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.

By definition, a new major version of a package is not backwards compatible with the previous version. This means a new major version of a module must have a different module path than the previous version. Starting with v2, the major version must appear at the end of the module path (declared in the module statement in the go.mod file). For example, when the authors of the module developed v2, they used the new module path Users who wanted to use v2 had to change their package imports and module requirements to

The need for major version suffixes is one of the ways Go modules differs from most other dependency management systems. Suffixes are needed to solve the diamond dependency problem. Before Go modules, allowed package maintainers to follow what we now refer to as the import compatibility rule. With, if you depend on a package that imports and another package that imports, there is no conflict because the two yaml packages have different import paths — they use a version suffix, as with Go modules. Since shares the same version suffix methodology as Go modules, the Go command accepts the .v2 in as a valid major version suffix. This is a special case for compatibility with modules hosted at other domains need a slash suffix like /v2.

Major version strategies

The recommended strategy is to develop v2+ modules in a directory named after the major version suffix. @ master branch
/go.mod    → module
/v2/go.mod → module

This approach is compatible with tools that aren't aware of modules: file paths within the repository match the paths expected by go get in GOPATH mode. This strategy also allows all major versions to be developed together in different directories.

Other strategies may keep major versions on separate branches. However, if v2+ source code is on the repository's default branch (usually master), tools that are not version-aware — including the go command in GOPATH mode — may not distinguish between major versions.

The examples in this post will follow the major version subdirectory strategy, since it provides the most compatibility. We recommend that module authors follow this strategy as long as they have users developing in GOPATH mode.

Publishing v2 and beyond

This post uses as an example:

$ pwd
$ ls  call_option.go  internal     gax.go          invoke.go
LICENSE             go.mod          tools.go           go.sum
$ cat go.mod

go 1.9

require ( v1.3.1 v0.0.0-20190221220918-438050ddec5e v0.0.0-20181026193005-c67002cb31c3 v0.0.0-20190114222345-bf090417da8b v1.19.0 v0.0.0-20190102054323-c2f93a96b099

To start development on v2 of, we'll create a new v2/ directory and copy our package into it.

$ mkdir v2
$ cp *.go v2/
building file list ... done

sent 10588 bytes  received 130 bytes  21436.00 bytes/sec
total size is 10208  speedup is 0.95

Now, let's create a v2 go.mod file by copying the current go.mod file and adding a v2/ suffix to the module path:

$ cp go.mod v2/go.mod
$ go mod edit -module v2/go.mod

Note that the v2 version is treated as a separate module from the v0 / v1 versions: both may coexist in the same build. So, 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. For example, to update all references to, you can use find and sed:

$ find . -type f \
    -name '*.go' \
    -exec sed -i -e 's,,,g' {} \;

Now we have a v2 module, but we want to experiment and make changes before publishing a release. Until we release v2.0.0 (or any version without a pre-release suffix), we can develop and make breaking changes as we decide on the new API. If we want users to be able to experiment with the new API before we officially make it stable, we can publish a v2 pre-release version:

$ git tag v2.0.0-alpha.1
$ git push origin v2.0.0-alpha.1

Once we are happy with our v2 API and are sure we don't need any other breaking changes, we can tag v2.0.0:

$ git tag v2.0.0
$ git push origin v2.0.0

At that point, there are now two major versions to maintain. Backwards compatible changes and bug fixes will lead to new minor and patch releases (for example, v1.1.0, v2.0.1, etc.).


Major version changes result in development and maintenance overhead and require investment from downstream users to migrate. The larger the project, the larger these overheads tend to be. A major version change should only come after identifying a compelling reason. Once a compelling reason has been identified for a breaking change, we recommend developing multiple major versions in the master branch because it is compatible with a wider variety of existing tools.

Breaking changes to a v1+ module should always happen in a new, vN+1 module. When a new module is released, it means additional work for the maintainers and for the users who need to migrate to the new package. Maintainers should therefore validate their APIs before making a stable release, and consider carefully whether breaking changes are really necessary beyond v1.

Working with Errors in Go 1.13

Damien Neil and Jonathan Amsterdam
17 October 2019


Go’s treatment of errors as values has served us well over the last decade. Although the standard library’s support for errors has been minimal—just the errors.New and fmt.Errorf functions, which produce errors that contain only a message—the built-in error interface allows Go programmers to add whatever information they desire. All it requires is a type that implements an Error method:

type QueryError struct {
    Query string
    Err   error

func (e *QueryError) Error() string { return e.Query + ": " + e.Err.Error() }

Error types like this one are ubiquitous, and the information they store varies widely, from timestamps to filenames to server addresses. Often, that information includes another, lower-level error to provide additional context.

The pattern of one error containing another is so pervasive in Go code that, after extensive discussion, Go 1.13 added explicit support for it. This post describes the additions to the standard library that provide that support: three new functions in the errors package, and a new formatting verb for fmt.Errorf.

Before describing the changes in detail, let's review how errors are examined and constructed in previous versions of the language.

Errors before Go 1.13

Examining errors

Go errors are values. Programs make decisions based on those values in a few ways. The most common is to compare an error to nil to see if an operation failed.

if err != nil {
    // something went wrong

Sometimes we compare an error to a known sentinel value, to see if a specific error has occurred.

var ErrNotFound = errors.New("not found")

if err == ErrNotFound {
    // something wasn't found

An error value may be of any type which satisfies the language-defined error interface. A program can use a type assertion or type switch to view an error value as a more specific type.

type NotFoundError struct {
    Name string

func (e *NotFoundError) Error() string { return e.Name + ": not found" }

if e, ok := err.(*NotFoundError); ok {
    // e.Name wasn't found

Adding information

Frequently a function passes an error up the call stack while adding information to it, like a brief description of what was happening when the error occurred. A simple way to do this is to construct a new error that includes the text of the previous one:

if err != nil {
    return fmt.Errorf("decompress %v: %v", name, err)

Creating a new error with fmt.Errorf discards everything from the original error except the text. As we saw above with QueryError, we may sometimes want to define a new error type that contains the underlying error, preserving it for inspection by code. Here is QueryError again:

type QueryError struct {
    Query string
    Err   error

Programs can look inside a *QueryError value to make decisions based on the underlying error. You'll sometimes see this referred to as "unwrapping" the error.

if e, ok := err.(*QueryError); ok && e.Err == ErrPermission {
    // query failed because of a permission problem

The os.PathError type in the standard library is another example of one error which contains another.

Errors in Go 1.13

The Unwrap method

Go 1.13 introduces new features to the errors and fmt standard library packages to simplify working with errors that contain other errors. The most significant of these is a convention rather than a change: an error which contains another may implement an Unwrap method returning the underlying error. If e1.Unwrap() returns e2, then we say that e1 wraps e2, and that you can unwrap e1 to get e2.

Following this convention, we can give the QueryError type above an Unwrap method that returns its contained error:

func (e *QueryError) Unwrap() error { return e.Err }

The result of unwrapping an error may itself have an Unwrap method; we call the sequence of errors produced by repeated unwrapping the error chain.

Examining errors with Is and As

The Go 1.13 errors package includes two new functions for examining errors: Is and As.

The errors.Is function compares an error to a value.

// Similar to:
//   if err == ErrNotFound { … }
if errors.Is(err, ErrNotFound) {
    // something wasn't found

The As function tests whether an error is a specific type.

// Similar to:
//   if e, ok := err.(*QueryError); ok { … }
var e *QueryError
if errors.As(err, &e) {
    // err is a *QueryError, and e is set to the error's value

In the simplest case, the errors.Is function behaves like a comparison to a sentinel error, and the errors.As function behaves like a type assertion. When operating on wrapped errors, however, these functions consider all the errors in a chain. Let's look again at the example from above of unwrapping a QueryError to examine the underlying error:

if e, ok := err.(*QueryError); ok && e.Err == ErrPermission {
    // query failed because of a permission problem

Using the errors.Is function, we can write this as:

if errors.Is(err, ErrPermission) {
    // err, or some error that it wraps, is a permission problem

The errors package also includes a new Unwrap function which returns the result of calling an error's Unwrap method, or nil when the error has no Unwrap method. It is usually better to use errors.Is or errors.As, however, since these functions will examine the entire chain in a single call.

Wrapping errors with %w

As mentioned earlier, it is common to use the fmt.Errorf function to add additional information to an error.

if err != nil {
    return fmt.Errorf("decompress %v: %v", name, err)

In Go 1.13, the fmt.Errorf function supports a new %w verb. When this verb is present, the error returned by fmt.Errorf will have an Unwrap method returning the argument of %w, which must be an error. In all other ways, %w is identical to %v.

if err != nil {
    // Return an error which unwraps to err.
    return fmt.Errorf("decompress %v: %w", name, err)

Wrapping an error with %w makes it available to errors.Is and errors.As:

err := fmt.Errorf("access denied: %w", ErrPermission)
if errors.Is(err, ErrPermission) ...

Whether to Wrap

When adding additional context to an error, either with fmt.Errorf or by implementing a custom type, you need to decide whether the new error should wrap the original. There is no single answer to this question; it depends on the context in which the new error is created. Wrap an error to expose it to callers. Do not wrap an error when doing so would expose implementation details.

As one example, imagine a Parse function which reads a complex data structure from an io.Reader. If an error occurs, we wish to report the line and column number at which it occurred. If the error occurs while reading from the io.Reader, we will want to wrap that error to allow inspection of the underlying problem. Since the caller provided the io.Reader to the function, it makes sense to expose the error produced by it.

In contrast, a function which makes several calls to a database probably should not return an error which unwraps to the result of one of those calls. If the database used by the function is an implementation detail, then exposing these errors is a violation of abstraction. For example, if the LookupUser function of your package pkg uses Go's database/sql package, then it may encounter a sql.ErrNoRows error. If you return that error with fmt.Errorf("accessing DB: %v", err) then a caller cannot look inside to find the sql.ErrNoRows. But if the function instead returns fmt.Errorf("accessing DB: %w", err), then a caller could reasonably write

err := pkg.LookupUser(...)
if errors.Is(err, sql.ErrNoRows) …

At that point, the function must always return sql.ErrNoRows if you don't want to break your clients, even if you switch to a different database package. In other words, wrapping an error makes that error part of your API. If you don't want to commit to supporting that error as part of your API in the future, you shouldn't wrap the error.

It’s important to remember that whether you wrap or not, the error text will be the same. A person trying to understand the error will have the same information either way; the choice to wrap is about whether to give programs additional information so they can make more informed decisions, or to withhold that information to preserve an abstraction layer.

Customizing error tests with Is and As methods

The errors.Is function examines each error in a chain for a match with a target value. By default, an error matches the target if the two are equal. In addition, an error in the chain may declare that it matches a target by implementing an Is method.

As an example, consider this error inspired by the Upspin error package which compares an error against a template, considering only fields which are non-zero in the template:

type Error struct {
    Path string
    User string

func (e *Error) Is(target error) bool {
    t, ok := target.(*Error)
    if !ok {
        return false
    return (e.Path == t.Path || t.Path == "") &&
           (e.User == t.User || t.User == "")

if errors.Is(err, &Error{User: "someuser"}) {
    // err's User field is "someuser".

The errors.As function similarly consults an As method when present.

Errors and package APIs

A package which returns errors (and most do) should describe what properties of those errors programmers may rely on. A well-designed package will also avoid returning errors with properties that should not be relied upon.

The simplest specification is to say that operations either succeed or fail, returning a nil or non-nil error value respectively. In many cases, no further information is needed.

If we wish a function to return an identifiable error condition, such as "item not found," we might return an error wrapping a sentinel.

var ErrNotFound = errors.New("not found")

// FetchItem returns the named item.
// If no item with the name exists, FetchItem returns an error
// wrapping ErrNotFound.
func FetchItem(name string) (*Item, error) {
    if itemNotFound(name) {
        return nil, fmt.Errorf("%q: %w", name, ErrNotFound)
    // ...

There are other existing patterns for providing errors which can be semantically examined by the caller, such as directly returning a sentinel value, a specific type, or a value which can be examined with a predicate function.

In all cases, care should be taken not to expose internal details to the user. As we touched on in "Whether to Wrap" above, when you return an error from another package you should convert the error to a form that does not expose the underlying error, unless you are willing to commit to returning that specific error in the future.

f, err := os.Open(filename)
if err != nil {
    // The *os.PathError returned by os.Open is an internal detail.
    // To avoid exposing it to the caller, repackage it as a new
    // error with the same text. We use the %v formatting verb, since
    // %w would permit the caller to unwrap the original *os.PathError.
    return fmt.Errorf("%v", err)

If a function is defined as returning an error wrapping some sentinel or type, do not return the underlying error directly.

var ErrPermission = errors.New("permission denied")

// DoSomething returns an error wrapping ErrPermission if the user
// does not have permission to do something.
func DoSomething() error {
    if !userHasPermission() {
        // If we return ErrPermission directly, callers might come
        // to depend on the exact error value, writing code like this:
        //     if err := pkg.DoSomething(); err == pkg.ErrPermission { … }
        // This will cause problems if we want to add additional
        // context to the error in the future. To avoid this, we
        // return an error wrapping the sentinel so that users must
        // always unwrap it:
        //     if err := pkg.DoSomething(); errors.Is(err, pkg.ErrPermission) { ... }
        return fmt.Errorf("%w", ErrPermission)
    // ...


Although the changes we’ve discussed amount to just three functions and a formatting verb, we hope they will go a long way toward improving how errors are handled in Go programs. We expect that wrapping to provide additional context will become commonplace, helping programs to make better decisions and helping programmers to find bugs more quickly.

As Russ Cox said in his GopherCon 2019 keynote, on the path to Go 2 we experiment, simplify and ship. Now that we’ve shipped these changes, we look forward to the experiments that will follow.

See the index for more articles.