跳到正文
awesomego.org

Common libraries

source ↗

Common libraries

Flags

Go programs in the Google codebase use an internal variant of the standard flag package. It has a similar interface but interoperates well with internal Google systems. Flag names in Go binaries should prefer to use underscores to separate words, though the variables that hold a flag’s value should follow the standard Go name style (mixed caps). Specifically, the flag name should be in snake case, and the variable name should be the equivalent name in camel case.

// Good:
var (
    pollInterval = flag.Duration("poll_interval", time.Minute, "Interval to use for polling.")
)
// Bad:
var (
    poll_interval = flag.Int("pollIntervalSeconds", 60, "Interval to use for polling in seconds.")
)

Flags must only be defined in package main or equivalent.

General-purpose packages should be configured using Go APIs, not by punching through to the command-line interface; don’t let importing a library export new flags as a side effect. That is, prefer explicit function arguments or struct field assignment or much less frequently and under the strictest of scrutiny exported global variables. In the extremely rare case that it is necessary to break this rule, the flag name must clearly indicate the package that it configures.

If your flags are global variables, place them in their own var group, following the imports section.

There is additional discussion around best practices for creating complex CLIs with subcommands.

See also:

Logging

Go programs in the Google codebase use a variant of the standard log package. It has a similar but more powerful interface and interoperates well with internal Google systems. An open source version of this library is available as package glog, and open source Google projects may use that, but this guide refers to it as log throughout.

Note: For abnormal program exits, this library uses log.Fatal to abort with a stacktrace, and log.Exit to stop without one. There is no log.Panic function as in the standard library.

Tip: log.Info(v) is equivalent log.Infof("%v", v), and the same goes for other logging levels. Prefer the non-formatting version when you have no formatting to do.

See also:

Contexts

Values of the context.Context type carry security credentials, tracing information, deadlines, and cancellation signals across API and process boundaries. Unlike C++ and Java, which in the Google codebase use thread-local storage, Go programs pass contexts explicitly along the entire function call chain from incoming RPCs and HTTP requests to outgoing requests.

When passed to a function or method, context.Context is always the first parameter.

func F(ctx context.Context /* other arguments */) {}

Exceptions are:

  • In an HTTP handler, where the context comes from req.Context().

  • In streaming RPC methods, where the context comes from the stream.

    Code using gRPC streaming accesses a context from a Context() method in the generated server type, which implements grpc.ServerStream. See gRPC Generated Code documentation.

  • In test functions (e.g. TestXXX, BenchmarkXXX, FuzzXXX), where the context comes from (testing.TB).Context().

  • In other entrypoint functions (see below for examples of such functions), use context.Background().

    • In binary targets: main
    • In general purpose code and libraries: init

Note: It is very rare for code in the middle of a callchain to require creating a base context of its own using context.Background(). Always prefer taking a context from your caller, unless it’s the wrong context.

You may come across server libraries (the implementation of Stubby, gRPC, or HTTP in Google’s server framework for Go) that construct a fresh context object per request. These contexts are immediately filled with information from the incoming request, so that when passed to the request handler, the context’s attached values have been propagated to it across the network boundary from the client caller. Moreover, these contexts’ lifetimes are scoped to that of the request: when the request is finished, the context is cancelled.

Unless you are implementing a server framework, you shouldn’t create contexts with context.Background() in library code. Instead, prefer using context detachment, which is mentioned below, if there is an existing context available. If you think you do need context.Background() outside of entrypoint functions, discuss it with the Google Go style mailing list before committing to an implementation.

The convention that context.Context comes first in functions also applies to test helpers.

// Good:
func readTestFile(ctx context.Context, t *testing.T, path string) string {}

Do not add a context member to a struct type. Instead, add a context parameter to each method on the type that needs to pass it along. The one exception is for methods whose signature must match an interface in the standard library or in a third party library outside Google’s control. Such cases are very rare, and should be discussed with the Google Go style mailing list before implementation and readability review.

Note: Go 1.24 added a (testing.TB).Context() method. In tests, prefer using (testing.TB).Context() over context.Background() to provide the initial context.Context used by the test. Helper functions, environment or test double setup, and other functions called from the test function body that require a context should have one explicitly passed.

Code in the Google codebase that must spawn background operations which can run after the parent context has been cancelled can use an internal package for detachment. Follow issue #40221 for discussions on an open source alternative.

Since contexts are immutable, it is fine to pass the same context to multiple calls that share the same deadline, cancellation signal, credentials, parent trace, and so on.

See also:

Custom contexts

Do not create custom context types or use interfaces other than context.Context in function signatures. There are no exceptions to this rule.

Imagine if every team had a custom context. Every function call from package p to package q would have to determine how to convert a p.Context to a q.Context, for all pairs of packages p and q. This is impractical and error-prone for humans, and it makes automated refactorings that add context parameters nearly impossible.

If you have application data to pass around, put it in a parameter, in the receiver, in globals, or in a Context value if it truly belongs there. Creating your own context type is not acceptable since it undermines the ability of the Go team to make Go programs work properly in production.

crypto/rand

Do not use package math/rand to generate keys, even throwaway ones. If unseeded, the generator is completely predictable. Seeded with time.Nanoseconds(), there are just a few bits of entropy. Instead, use crypto/rand’s Reader, and if you need text, print to hexadecimal or base64.

// Good:
import (
    "crypto/rand"
    // "encoding/base64"
    // "encoding/hex"
    "fmt"

    // ...
)

func Key() string {
    buf := make([]byte, 16)
    if _, err := rand.Read(buf); err != nil {
        log.Fatalf("Out of randomness, should never happen: %v", err)
    }
    return fmt.Sprintf("%x", buf)
    // or hex.EncodeToString(buf)
    // or base64.StdEncoding.EncodeToString(buf)
}

Note: log.Fatalf is not the standard library log. See [#logging].