mirror of
https://github.com/tiennm99/styleguide.git
synced 2026-10-13 10:15:59 +00:00
go: Export the latest version of internal guide. (#888)
* go: Export the latest version of internal guide. The updates encompass copy editing, enchanced guidance on package sizing, fixing broken hyperlinks, simplifying Protocol Buffer message guidance, clarifying context root behavior after (testing.TB).Context, and disjoint table test.
This commit is contained in:
1 parent
c94562358b
commit
b30e180a54
2 files changed
+148
-36
No files matched your search
+64
-20
@@ -97,7 +97,7 @@ the name will be read. Consider the following recommendations to avoid excess
|
||||
|
||||
```go
|
||||
// Bad:
|
||||
func TransformYAMLToJSON(input *Config) *jsonconfig.Config
|
||||
func TransformToJSON(input *Config) *jsonconfig.Config
|
||||
```
|
||||
|
||||
```go
|
||||
@@ -607,7 +607,7 @@ likely make that package too large. When something is conceptually distinct,
|
||||
giving it its own small package can make it easier to use. The short name of the
|
||||
package as known to clients together with the exported type name work together
|
||||
to make a meaningful identifier: e.g. `bytes.Buffer`, `ring.New`. The
|
||||
[blog post][blog-pkg-names] has more examples.
|
||||
[Package Names blog post][blog-pkg-names] has more examples.
|
||||
|
||||
Go style is flexible about file size, because maintainers can move code within a
|
||||
package from one file to another without affecting callers. But as a general
|
||||
@@ -627,13 +627,60 @@ code is different than it is in open source Go projects: you can have multiple
|
||||
`go_library` targets in a single directory. A good reason to give each package
|
||||
its own directory is if you expect to open source your project in the future.
|
||||
|
||||
A few non-canonical reference examples to help demonstrate these ideas in
|
||||
action:
|
||||
|
||||
* small packages that contain one cohesive idea that warrant nothing more
|
||||
being added nor nothing being removed:
|
||||
|
||||
* [package `csv`][package `csv`]: CSV data encoding and decoding with
|
||||
responsibility split respectively between [reader.go] and [writer.go].
|
||||
* [package `expvar`][package `expvar`]: whitebox program telemetry all
|
||||
contained in [expvar.go].
|
||||
|
||||
* moderately sized packages that contain one large domain and its multiple
|
||||
responsibilities together:
|
||||
|
||||
* [package `flag`][package `flag`]: command line flag management all
|
||||
contained in [flag.go].
|
||||
|
||||
* large packages that divide several closely related domains across several
|
||||
files:
|
||||
|
||||
* [package `http`][package `http`]: the core of HTTP:
|
||||
[client.go][http-client], support for HTTP clients;
|
||||
[server.go][http-client], support for HTTP servers; [cookie.go], cookie
|
||||
management.
|
||||
* [package `os`][package `os`]: cross-platform operating system
|
||||
abstractions: [exec.go], subprocess management; [file.go], file
|
||||
management; [tempfile.go], temporary files.
|
||||
|
||||
See also:
|
||||
|
||||
* [Test double packages](#naming-doubles)
|
||||
* [Organizing Go Code (Blog Post)]
|
||||
* [Organizing Go Code (Presentation)]
|
||||
|
||||
[blog-pkg-names]: https://go.dev/blog/package-names
|
||||
[package `bytes`]: https://go.dev/src/bytes/
|
||||
[Organizing Go Code (Blog Post)]: https://go.dev/blog/organizing-go-code
|
||||
[Organizing Go Code (Presentation)]: https://go.dev/talks/2014/organizeio.slide
|
||||
[package `csv`]: https://pkg.go.dev/encoding/csv
|
||||
[reader.go]: https://go.googlesource.com/go/+/refs/heads/master/src/encoding/csv/reader.go
|
||||
[writer.go]: https://go.googlesource.com/go/+/refs/heads/master/src/encoding/csv/writer.go
|
||||
[package `expvar`]: https://pkg.go.dev/expvar
|
||||
[expvar.go]: https://go.googlesource.com/go/+/refs/heads/master/src/expvar/expvar.go
|
||||
[package `flag`]: https://pkg.go.dev/flag
|
||||
[flag.go]: https://go.googlesource.com/go/+/refs/heads/master/src/flag/flag.go
|
||||
[godoc]: https://pkg.go.dev/
|
||||
[package `http`]: https://pkg.go.dev/net/http
|
||||
[http-client]: https://go.googlesource.com/go/+/refs/heads/master/src/net/http/client.go
|
||||
[http-server]: https://go.googlesource.com/go/+/refs/heads/master/src/net/http/server.go
|
||||
[cookie.go]: https://go.googlesource.com/go/+/refs/heads/master/src/net/http/cookie.go
|
||||
[package `os`]: https://pkg.go.dev/os
|
||||
[exec.go]: https://go.googlesource.com/go/+/refs/heads/master/src/os/exec.go
|
||||
[file.go]: https://go.googlesource.com/go/+/refs/heads/master/src/os/file.go
|
||||
[tempfile.go]: https://go.googlesource.com/go/+/refs/heads/master/src/os/tempfile.go
|
||||
|
||||
<a id="imports"></a>
|
||||
|
||||
@@ -641,7 +688,7 @@ See also:
|
||||
|
||||
<a id="import-protos"></a>
|
||||
|
||||
### Protos and stubs
|
||||
### Protocol Buffer Messages and Stubs
|
||||
|
||||
Proto library imports are treated differently than standard Go imports due to
|
||||
their cross-language nature. The convention for renamed proto imports are based
|
||||
@@ -650,35 +697,32 @@ on the rule that generated the package:
|
||||
* The `pb` suffix is generally used for `go_proto_library` rules.
|
||||
* The `grpc` suffix is generally used for `go_grpc_library` rules.
|
||||
|
||||
Generally, a short one- or two-letter prefix is used:
|
||||
Often a single word describing the package is used:
|
||||
|
||||
```go
|
||||
// Good:
|
||||
import (
|
||||
fspb "path/to/package/foo_service_go_proto"
|
||||
fsgrpc "path/to/package/foo_service_go_grpc"
|
||||
foopb "path/to/package/foo_service_go_proto"
|
||||
foogrpc "path/to/package/foo_service_go_grpc"
|
||||
)
|
||||
```
|
||||
|
||||
If there is only a single proto used by a package or the package is tied closely
|
||||
to that proto, the prefix can be omitted:
|
||||
|
||||
import ( pb "path/to/package/foo_service_go_proto" grpc
|
||||
"path/to/package/foo_service_go_grpc" )
|
||||
|
||||
If the symbols in the proto are generic or are not very self-descriptive, or if
|
||||
shortening the package name with an acronym is unclear, a short word can suffice
|
||||
as the prefix:
|
||||
Follow the style guidance for
|
||||
[package names](https://google.github.io/styleguide/go/decisions#package-names).
|
||||
Prefer whole words. Short names are good, but avoid ambiguity. When in doubt,
|
||||
use the proto package name up to _go with a pb suffix:
|
||||
|
||||
```go
|
||||
// Good:
|
||||
import (
|
||||
mapspb "path/to/package/maps_go_proto"
|
||||
pushqueueservicepb "path/to/package/push_queue_service_go_proto"
|
||||
)
|
||||
```
|
||||
|
||||
In this case `mapspb.Address` might be clearer than `mpb.Address` if the code in
|
||||
question is not already clearly related to maps.
|
||||
**Note:** Previous guidance encouraged very short names such as "xpb" or even
|
||||
just "pb". New code should prefer more descriptive names. Existing code which
|
||||
uses short names should not be used as an example, but does not need to be
|
||||
changed.
|
||||
|
||||
<a id="import-order"></a>
|
||||
|
||||
@@ -1983,7 +2027,7 @@ Using an option structure has a number of benefits:
|
||||
* The struct literal includes both fields and values for each argument, which
|
||||
makes them self-documenting and harder to swap.
|
||||
* Irrelevant or "default" fields can be omitted.
|
||||
* Callers can share the options struct and write helpers to operate on it.
|
||||
* Callers can share the option struct and write helpers to operate on it.
|
||||
* Structs provide cleaner per-field documentation than function arguments.
|
||||
* Option structs can grow over time without impacting call-sites.
|
||||
|
||||
@@ -3199,7 +3243,7 @@ package main
|
||||
func main() {
|
||||
sidecars := sidecar.New()
|
||||
if err := sidecars.Register("Cloud Logger", cloudlogger.New()); err != nil {
|
||||
log.Exitf("could not setup cloud logger: %v", err)
|
||||
log.Exitf("Could not setup cloud logger: %v", err)
|
||||
}
|
||||
cfg := &myapp.Config{Sidecars: sidecars}
|
||||
myapp.Run(context.Background(), cfg)
|
||||
|
||||
+84
-16
@@ -69,6 +69,9 @@ exceptions to this principle:
|
||||
reuse identifiers, as is done in [`syscall`]. This is expected to be very
|
||||
rare in most codebases.
|
||||
|
||||
**Note:** Filenames of source code are not Go identifiers and do not have to
|
||||
follow these conventions. They may contain underscores.
|
||||
|
||||
[`syscall`]: https://pkg.go.dev/syscall#pkg-constants
|
||||
|
||||
<a id="package-names"></a>
|
||||
@@ -1065,7 +1068,7 @@ can get wrapped into an interface and thus become a non-nil value (see the
|
||||
func Bad() *os.PathError { /*...*/ }
|
||||
```
|
||||
|
||||
**Tip**: A function that takes a `context.Context` argument should usually
|
||||
**Tip:** A function that takes a [`context.Context`] argument should usually
|
||||
return an `error` so that the caller can determine if the context was cancelled
|
||||
while the function was running.
|
||||
|
||||
@@ -2178,7 +2181,7 @@ the scope of a function and factoring out the logic into
|
||||
important to document when and why the goroutines exit.
|
||||
|
||||
Code that follows best practices around context usage often helps make this
|
||||
clear. It is conventionally managed with a `context.Context`:
|
||||
clear. It is conventionally managed with a [`context.Context`]:
|
||||
|
||||
```go
|
||||
// Good:
|
||||
@@ -2776,7 +2779,7 @@ chain from incoming RPCs and HTTP requests to outgoing requests.
|
||||
|
||||
[`context.Context`]: https://pkg.go.dev/context
|
||||
|
||||
When passed to a function or method, `context.Context` is always the first
|
||||
When passed to a function or method, [`context.Context`] is always the first
|
||||
parameter.
|
||||
|
||||
```go
|
||||
@@ -2794,15 +2797,16 @@ Exceptions are:
|
||||
[gRPC Generated Code documentation](https://grpc.io/docs/languages/go/generated-code/).
|
||||
|
||||
* In entrypoint functions (see below for examples of such functions), use
|
||||
[`context.Background()`](https://pkg.go.dev/context/#Background).
|
||||
[`context.Background()`] or, for tests,
|
||||
[`tb.Context()`](https://pkg.go.dev/testing#TB.Context).
|
||||
|
||||
* In binary targets: `main`
|
||||
* In general purpose code and libraries: `init`
|
||||
* In tests: `TestXXX`, `BenchmarkXXX`, `FuzzXXX`
|
||||
|
||||
> **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.
|
||||
> 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
|
||||
@@ -2814,13 +2818,13 @@ Exceptions are:
|
||||
> cancelled.
|
||||
>
|
||||
> Unless you are implementing a server framework, you shouldn't create contexts
|
||||
> with `context.Background()` in library code. Instead, prefer using context
|
||||
> 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
|
||||
> 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
|
||||
The convention that [`context.Context`] comes first in functions also applies to
|
||||
test helpers.
|
||||
|
||||
```go
|
||||
@@ -2835,6 +2839,14 @@ 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.
|
||||
|
||||
[`(testing.TB).Context()`]: https://pkg.go.dev/testing#TB.Context
|
||||
|
||||
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](https://github.com/golang/go/issues/40221) for
|
||||
@@ -2848,6 +2860,7 @@ See also:
|
||||
|
||||
* [Contexts and structs]
|
||||
|
||||
[`context.Background()`]: https://pkg.go.dev/context/#Background
|
||||
[Contexts and structs]: https://go.dev/blog/context-and-structs
|
||||
|
||||
<a id="custom-contexts"></a>
|
||||
@@ -2855,7 +2868,8 @@ 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.
|
||||
[`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
|
||||
@@ -3511,12 +3525,66 @@ func TestCompare(t *testing.T) {
|
||||
[identify the row numerically](#table-tests-identifying-the-row).
|
||||
|
||||
When some test cases need to be checked using different logic from other test
|
||||
cases, it is more appropriate to write multiple test functions, as explained in
|
||||
[GoTip #50: Disjoint Table Tests]. The logic of your test code can get difficult
|
||||
to understand when each entry in a table has its own different conditional logic
|
||||
to check each output for its inputs. If test cases have different logic but
|
||||
identical setup, a sequence of [subtests](#subtests) within a single test
|
||||
function might make sense.
|
||||
cases, it is appropriate to write multiple test functions, as explained in
|
||||
[GoTip #50: Disjoint Table Tests].
|
||||
|
||||
When the additional test cases are simple (e.g., basic error checking) and don't
|
||||
introduce conditionalized code flow in the table test's loop body, it's
|
||||
permissible to include that case in the existing test, though be careful using
|
||||
logic like this. What starts simple today can organically grow into something
|
||||
unmaintainable.
|
||||
|
||||
For example:
|
||||
|
||||
```go
|
||||
func TestDivide(t *testing.T) {
|
||||
tests := []struct {
|
||||
dividend, divisor int
|
||||
want int
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
dividend: 4,
|
||||
divisor: 2,
|
||||
want: 2,
|
||||
},
|
||||
{
|
||||
dividend: 10,
|
||||
divisor: 2,
|
||||
want: 5,
|
||||
},
|
||||
{
|
||||
dividend: 1,
|
||||
divisor: 0,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
got, err := Divide(test.dividend, test.divisor)
|
||||
if (err != nil) != test.wantErr {
|
||||
t.Errorf("Divide(%d, %d) error = %v, want error presence = %t", test.dividend, test.divisor, err, test.wantErr)
|
||||
}
|
||||
|
||||
// In this example, we're only testing the value result when the tested function didn't fail.
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if got != test.want {
|
||||
t.Errorf("Divide(%d, %d) = %d, want %d", test.dividend, test.divisor, got, test.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
More complicated logic in your test code, like complex error checking based on
|
||||
conditional differences in test setup (often based on table test input
|
||||
parameters), can be [difficult to understand](guide#maintainability) when each
|
||||
entry in a table has specialized logic based on the inputs. If test cases have
|
||||
different logic but identical setup, a sequence of [subtests](#subtests) within
|
||||
a single test function might be more readable. A test helper may also be useful
|
||||
for simplifying test setup in order to maintain the readability of a test body.
|
||||
|
||||
You can combine table-driven tests with multiple test functions. For example,
|
||||
when testing that a function's output exactly matches the expected output and
|
||||
|
||||
Reference in new issue
Block a user