-
Notifications
You must be signed in to change notification settings - Fork 13.3k
/
best-practices.md
3513 lines (2719 loc) · 116 KB
/
best-practices.md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!--* toc_depth: 3 *-->
# Go Style Best Practices
https://google.github.io/styleguide/go/best-practices
[Overview](index) | [Guide](guide) | [Decisions](decisions) |
[Best practices](best-practices)
<!--
-->
{% raw %}
**Note:** This is part of a series of documents that outline [Go Style](index)
at Google. This document is **neither [normative](index#normative) nor
[canonical](index#canonical)**, and is an auxiliary document to the
[core style guide](guide). See [the overview](index#about) for more information.
<a id="about"></a>
## About
This file documents **guidance about how to best apply the Go Style Guide**.
This guidance is intended for common situations that arise frequently, but may
not apply in every circumstance. Where possible, multiple alternative approaches
are discussed along with the considerations that go into the decision about when
and when not to apply them.
See [the overview](index#about) for the full set of Style Guide documents.
<a id="naming"></a>
## Naming
<a id="function-names"></a>
### Function and method names
<a id="function-name-repetition"></a>
#### Avoid repetition
When choosing the name for a function or method, consider the context in which
the name will be read. Consider the following recommendations to avoid excess
[repetition](decisions#repetition) at the call site:
* The following can generally be omitted from function and method names:
* The types of the inputs and outputs (when there is no collision)
* The type of a method's receiver
* Whether an input or output is a pointer
* For functions, do not
[repeat the name of the package](decisions#repetitive-with-package).
```go
// Bad:
package yamlconfig
func ParseYAMLConfig(input string) (*Config, error)
```
```go
// Good:
package yamlconfig
func Parse(input string) (*Config, error)
```
* For methods, do not repeat the name of the method receiver.
```go
// Bad:
func (c *Config) WriteConfigTo(w io.Writer) (int64, error)
```
```go
// Good:
func (c *Config) WriteTo(w io.Writer) (int64, error)
```
* Do not repeat the names of variables passed as parameters.
```go
// Bad:
func OverrideFirstWithSecond(dest, source *Config) error
```
```go
// Good:
func Override(dest, source *Config) error
```
* Do not repeat the names and types of the return values.
```go
// Bad:
func TransformYAMLToJSON(input *Config) *jsonconfig.Config
```
```go
// Good:
func Transform(input *Config) *jsonconfig.Config
```
When it is necessary to disambiguate functions of a similar name, it is
acceptable to include extra information.
```go
// Good:
func (c *Config) WriteTextTo(w io.Writer) (int64, error)
func (c *Config) WriteBinaryTo(w io.Writer) (int64, error)
```
<a id="function-name-conventions"></a>
#### Naming conventions
There are some other common conventions when choosing names for functions and
methods:
* Functions that return something are given noun-like names.
```go
// Good:
func (c *Config) JobName(key string) (value string, ok bool)
```
A corollary of this is that function and method names should
[avoid the prefix `Get`](decisions#getters).
```go
// Bad:
func (c *Config) GetJobName(key string) (value string, ok bool)
```
* Functions that do something are given verb-like names.
```go
// Good:
func (c *Config) WriteDetail(w io.Writer) (int64, error)
```
* Identical functions that differ only by the types involved include the name
of the type at the end of the name.
```go
// Good:
func ParseInt(input string) (int, error)
func ParseInt64(input string) (int64, error)
func AppendInt(buf []byte, value int) []byte
func AppendInt64(buf []byte, value int64) []byte
```
If there is a clear "primary" version, the type can be omitted from the name
for that version:
```go
// Good:
func (c *Config) Marshal() ([]byte, error)
func (c *Config) MarshalText() (string, error)
```
<a id="naming-doubles"></a>
### Test double packages and types
There are several disciplines you can apply to [naming] packages and types that
provide test helpers and especially [test doubles]. A test double could be a
stub, fake, mock, or spy.
These examples mostly use stubs. Update your names accordingly if your code uses
fakes or another kind of test double.
[naming]: guide#naming
[test doubles]: https://abseil.io/resources/swe-book/html/ch13.html#basic_concepts
Suppose you have a well-focused package providing production code similar to
this:
```go
package creditcard
import (
"errors"
"path/to/money"
)
// ErrDeclined indicates that the issuer declines the charge.
var ErrDeclined = errors.New("creditcard: declined")
// Card contains information about a credit card, such as its issuer,
// expiration, and limit.
type Card struct {
// omitted
}
// Service allows you to perform operations with credit cards against external
// payment processor vendors like charge, authorize, reimburse, and subscribe.
type Service struct {
// omitted
}
func (s *Service) Charge(c *Card, amount money.Money) error { /* omitted */ }
```
<a id="naming-doubles-helper-package"></a>
#### Creating test helper packages
Suppose you want to create a package that contains test doubles for another.
We'll use `package creditcard` (from above) for this example:
One approach is to introduce a new Go package based on the production one for
testing. A safe choice is to append the word `test` to the original package name
("creditcard" + "test"):
```go
// Good:
package creditcardtest
```
Unless stated explicitly otherwise, all examples in the sections below are in
`package creditcardtest`.
<a id="naming-doubles-simple"></a>
#### Simple case
You want to add a set of test doubles for `Service`. Because `Card` is
effectively a dumb data type, similar to a Protocol Buffer message, it needs no
special treatment in tests, so no double is required. If you anticipate only
test doubles for one type (like `Service`), you can take a concise approach to
naming the doubles:
```go
// Good:
import (
"path/to/creditcard"
"path/to/money"
)
// Stub stubs creditcard.Service and provides no behavior of its own.
type Stub struct{}
func (Stub) Charge(*creditcard.Card, money.Money) error { return nil }
```
This is strictly preferable to a naming choice like `StubService` or the very
poor `StubCreditCardService`, because the base package name and its domain types
imply what `creditcardtest.Stub` is.
Finally, if the package is built with Bazel, make sure the new `go_library` rule
for the package is marked as `testonly`:
```build
# Good:
go_library(
name = "creditcardtest",
srcs = ["creditcardtest.go"],
deps = [
":creditcard",
":money",
],
testonly = True,
)
```
The approach above is conventional and will be reasonably well understood by
other engineers.
See also:
* [Go Tip #42: Authoring a Stub for Testing](https://google.github.io/styleguide/go/index.html#gotip)
<a id="naming-doubles-multiple-behaviors"></a>
#### Multiple test double behaviors
When one kind of stub is not enough (for example, you also need one that always
fails), we recommend naming the stubs according to the behavior they emulate.
Here we rename `Stub` to `AlwaysCharges` and introduce a new stub called
`AlwaysDeclines`:
```go
// Good:
// AlwaysCharges stubs creditcard.Service and simulates success.
type AlwaysCharges struct{}
func (AlwaysCharges) Charge(*creditcard.Card, money.Money) error { return nil }
// AlwaysDeclines stubs creditcard.Service and simulates declined charges.
type AlwaysDeclines struct{}
func (AlwaysDeclines) Charge(*creditcard.Card, money.Money) error {
return creditcard.ErrDeclined
}
```
<a id="naming-doubles-multiple-types"></a>
#### Multiple doubles for multiple types
But now suppose that `package creditcard` contains multiple types worth creating
doubles for, as seen below with `Service` and `StoredValue`:
```go
package creditcard
type Service struct {
// omitted
}
type Card struct {
// omitted
}
// StoredValue manages customer credit balances. This applies when returned
// merchandise is credited to a customer's local account instead of processed
// by the credit issuer. For this reason, it is implemented as a separate
// service.
type StoredValue struct {
// omitted
}
func (s *StoredValue) Credit(c *Card, amount money.Money) error { /* omitted */ }
```
In this case, more explicit test double naming is sensible:
```go
// Good:
type StubService struct{}
func (StubService) Charge(*creditcard.Card, money.Money) error { return nil }
type StubStoredValue struct{}
func (StubStoredValue) Credit(*creditcard.Card, money.Money) error { return nil }
```
<a id="naming-doubles-local-variables"></a>
#### Local variables in tests
When variables in your tests refer to doubles, choose a name that most clearly
differentiates the double from other production types based on context. Consider
some production code you want to test:
```go
package payment
import (
"path/to/creditcard"
"path/to/money"
)
type CreditCard interface {
Charge(*creditcard.Card, money.Money) error
}
type Processor struct {
CC CreditCard
}
var ErrBadInstrument = errors.New("payment: instrument is invalid or expired")
func (p *Processor) Process(c *creditcard.Card, amount money.Money) error {
if c.Expired() {
return ErrBadInstrument
}
return p.CC.Charge(c, amount)
}
```
In the tests, a test double called a "spy" for `CreditCard` is juxtaposed
against production types, so prefixing the name may improve clarity.
```go
// Good:
package payment
import "path/to/creditcardtest"
func TestProcessor(t *testing.T) {
var spyCC creditcardtest.Spy
proc := &Processor{CC: spyCC}
// declarations omitted: card and amount
if err := proc.Process(card, amount); err != nil {
t.Errorf("proc.Process(card, amount) = %v, want nil", err)
}
charges := []creditcardtest.Charge{
{Card: card, Amount: amount},
}
if got, want := spyCC.Charges, charges; !cmp.Equal(got, want) {
t.Errorf("spyCC.Charges = %v, want %v", got, want)
}
}
```
This is clearer than when the name is not prefixed.
```go
// Bad:
package payment
import "path/to/creditcardtest"
func TestProcessor(t *testing.T) {
var cc creditcardtest.Spy
proc := &Processor{CC: cc}
// declarations omitted: card and amount
if err := proc.Process(card, amount); err != nil {
t.Errorf("proc.Process(card, amount) = %v, want nil", err)
}
charges := []creditcardtest.Charge{
{Card: card, Amount: amount},
}
if got, want := cc.Charges, charges; !cmp.Equal(got, want) {
t.Errorf("cc.Charges = %v, want %v", got, want)
}
}
```
<a id="shadowing"></a>
### Shadowing
**Note:** This explanation uses two informal terms, *stomping* and *shadowing*.
They are not official concepts in the Go language spec.
Like many programming languages, Go has mutable variables: assigning to a
variable changes its value.
```go
// Good:
func abs(i int) int {
if i < 0 {
i *= -1
}
return i
}
```
When using [short variable declarations] with the `:=` operator, in some cases a
new variable is not created. We can call this *stomping*. It's OK to do this
when the original value is no longer needed.
```go
// Good:
// innerHandler is a helper for some request handler, which itself issues
// requests to other backends.
func (s *Server) innerHandler(ctx context.Context, req *pb.MyRequest) *pb.MyResponse {
// Unconditionally cap the deadline for this part of request handling.
ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
defer cancel()
ctxlog.Info(ctx, "Capped deadline in inner request")
// Code here no longer has access to the original context.
// This is good style if when first writing this, you anticipate
// that even as the code grows, no operation legitimately should
// use the (possibly unbounded) original context that the caller provided.
// ...
}
```
Be careful using short variable declarations in a new scope, though: that
introduces a new variable. We can call this *shadowing* the original variable.
Code after the end of the block refers to the original. Here is a buggy attempt
to shorten the deadline conditionally:
```go
// Bad:
func (s *Server) innerHandler(ctx context.Context, req *pb.MyRequest) *pb.MyResponse {
// Attempt to conditionally cap the deadline.
if *shortenDeadlines {
ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
defer cancel()
ctxlog.Info(ctx, "Capped deadline in inner request")
}
// BUG: "ctx" here again means the context that the caller provided.
// The above buggy code compiled because both ctx and cancel
// were used inside the if statement.
// ...
}
```
A correct version of the code might be:
```go
// Good:
func (s *Server) innerHandler(ctx context.Context, req *pb.MyRequest) *pb.MyResponse {
if *shortenDeadlines {
var cancel func()
// Note the use of simple assignment, = and not :=.
ctx, cancel = context.WithTimeout(ctx, 3*time.Second)
defer cancel()
ctxlog.Info(ctx, "Capped deadline in inner request")
}
// ...
}
```
In the case we called stomping, because there's no new variable, the type being
assigned must match that of the original variable. With shadowing, an entirely
new entity is introduced so it can have a different type. Intentional shadowing
can be a useful practice, but you can always use a new name if it improves
[clarity](guide#clarity).
It is not a good idea to use variables with the same name as standard packages
other than very small scopes, because that renders free functions and values
from that package inaccessible. Conversely, when picking a name for your
package, avoid names that are likely to require
[import renaming](decisions#import-renaming) or cause shadowing of otherwise
good variable names at the client side.
```go
// Bad:
func LongFunction() {
url := "https://example.com/"
// Oops, now we can't use net/url in code below.
}
```
[short variable declarations]: https://go.dev/ref/spec#Short_variable_declarations
<a id="util-packages"></a>
### Util packages
Go packages have a name specified on the `package` declaration, separate from
the import path. The package name matters more for readability than the path.
Go package names should be
[related to what the package provides](decisions#package-names). Naming a
package just `util`, `helper`, `common` or similar is usually a poor choice (it
can be used as *part* of the name though). Uninformative names make the code
harder to read, and if used too broadly they are liable to cause needless
[import conflicts](decisions#import-renaming).
Instead, consider what the callsite will look like.
```go
// Good:
db := spannertest.NewDatabaseFromFile(...)
_, err := f.Seek(0, io.SeekStart)
b := elliptic.Marshal(curve, x, y)
```
You can tell roughly what each of these do even without knowing the imports list
(`cloud.google.com/go/spanner/spannertest`, `io`, and `crypto/elliptic`). With
less focused names, these might read:
```go
// Bad:
db := test.NewDatabaseFromFile(...)
_, err := f.Seek(0, common.SeekStart)
b := helper.Marshal(curve, x, y)
```
<a id="package-size"></a>
## Package size
If you're asking yourself how big your Go packages should be and whether to
place related types in the same package or split them into different ones, a
good place to start is the [Go blog post about package names][blog-pkg-names].
Despite the post title, it's not solely about naming. It contains some helpful
hints and cites several useful articles and talks.
Here are some other considerations and notes.
Users see [godoc] for the package in one page, and any methods exported by types
supplied by the package are grouped by their type. Godoc also group constructors
along with the types they return. If *client code* is likely to need two values
of different type to interact with each other, it may be convenient for the user
to have them in the same package.
Code within a package can access unexported identifiers in the package. If you
have a few related types whose *implementation* is tightly coupled, placing them
in the same package lets you achieve this coupling without polluting the public
API with these details. A good test for this coupling is to imagine a
hypothetical user of two packages, where the packages cover closely related
topics: if the user must import both packages in order to use either in any
meaningful way, combining them together is usually the right thing to do. The
standard library generally demonstrates this kind of scoping and layering well.
All of that being said, putting your entire project in a single package would
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.
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
guideline: it is usually not a good idea to have a single file with many
thousands of lines in it, or having many tiny files. There is no "one type, one
file" convention as in some other languages. As a rule of thumb, files should be
focused enough that a maintainer can tell which file contains something, and the
files should be small enough that it will be easy to find once there. The
standard library often splits large packages to several source files, grouping
related code by file. The source for [package `bytes`] is a good example.
Packages with long package documentation may choose to dedicate one file called
`doc.go` that has the [package documentation](decisions#package-comments), a
package declaration, and nothing else, but this is not required.
Within the Google codebase and in projects using Bazel, directory layout for Go
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.
See also:
* [Test double packages](#naming-doubles)
[blog-pkg-names]: https://go.dev/blog/package-names
[package `bytes`]: https://go.dev/src/bytes/
[godoc]: https://pkg.go.dev/
<a id="imports"></a>
## Imports
<a id="import-protos"></a>
### Protos 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
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:
```go
// Good:
import (
fspb "path/to/package/foo_service_go_proto"
fsgrpc "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:
```go
// Good:
import (
mapspb "path/to/package/maps_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.
<a id="import-order"></a>
### Import ordering
Imports are typically grouped into the following two (or more) blocks, in order:
1. Standard library imports (e.g., `"fmt"`)
1. imports (e.g., "/path/to/somelib")
1. (optional) Protobuf imports (e.g., `fpb "path/to/foo_go_proto"`)
1. (optional) Side-effect imports (e.g., `_ "path/to/package"`)
If a file does not have a group for one of the optional categories above, the
relevant imports are included in the project import group.
Any import grouping that is clear and easy to understand is generally fine. For
example, a team may choose to group gRPC imports separately from protobuf
imports.
> **Note:** For code maintaining only the two mandatory groups (one group for
> the standard library and one for all other imports), the `goimports` tool
> produces output consistent with this guidance.
>
> However, `goimports` has no knowledge of groups beyond the mandatory ones; the
> optional groups are prone to invalidation by the tool. When optional groups
> are used, attention on the part of both authors and reviewers is required to
> ensure that groupings remain compliant.
>
> Either approach is fine, but do not leave the imports section in an
> inconsistent, partially grouped state.
<a id="error-handling"></a>
## Error handling
In Go, [errors are values]; they are created by code and consumed by code.
Errors can be:
* Converted into diagnostic information for display to humans
* Used by the maintainer
* Interpreted by an end user
Error messages also show up across a variety of different surfaces including log
messages, error dumps, and rendered UIs.
Code that processes (produces or consumes) errors should do so deliberately. It
can be tempting to ignore or blindly propagate an error return value. However,
it is always worth considering whether the current function in the call frame is
positioned to handle the error most effectively. This is a large topic and it is
hard to give categorical advice. Use your judgment, but keep the following
considerations in mind:
* When creating an error value, decide whether to give it any
[structure](#error-structure).
* When handling an error, consider [adding information](#error-extra-info)
that you have but that the caller and/or callee might not.
* See also guidance on [error logging](#error-logging).
While it is usually not appropriate to ignore an error, a reasonable exception
to this is when orchestrating related operations, where often only the first
error is useful. Package [`errgroup`] provides a convenient abstraction for a
group of operations that can all fail or be canceled as a group.
[errors are values]: https://go.dev/blog/errors-are-values
[`errgroup`]: https://pkg.go.dev/golang.org/x/sync/errgroup
See also:
* [Effective Go on errors](https://go.dev/doc/effective_go#errors)
* [A post by the Go Blog on errors](https://go.dev/blog/go1.13-errors)
* [Package `errors`](https://pkg.go.dev/errors)
* [Package `upspin.io/errors`](https://commandcenter.blogspot.com/2017/12/error-handling-in-upspin.html)
* [GoTip #89: When to Use Canonical Status Codes as Errors](https://google.github.io/styleguide/go/index.html#gotip)
* [GoTip #48: Error Sentinel Values](https://google.github.io/styleguide/go/index.html#gotip)
* [GoTip #13: Designing Errors for Checking](https://google.github.io/styleguide/go/index.html#gotip)
<a id="error-structure"></a>
### Error structure
If callers need to interrogate the error (e.g., distinguish different error
conditions), give the error value structure so that this can be done
programmatically rather than having the caller perform string matching. This
advice applies to production code as well as to tests that care about different
error conditions.
The simplest structured errors are unparameterized global values.
```go
type Animal string
var (
// ErrDuplicate occurs if this animal has already been seen.
ErrDuplicate = errors.New("duplicate")
// ErrMarsupial occurs because we're allergic to marsupials outside Australia.
// Sorry.
ErrMarsupial = errors.New("marsupials are not supported")
)
func process(animal Animal) error {
switch {
case seen[animal]:
return ErrDuplicate
case marsupial(animal):
return ErrMarsupial
}
seen[animal] = true
// ...
return nil
}
```
The caller can simply compare the returned error value of the function with one
of the known error values:
```go
// Good:
func handlePet(...) {
switch err := process(an); err {
case ErrDuplicate:
return fmt.Errorf("feed %q: %v", an, err)
case ErrMarsupial:
// Try to recover with a friend instead.
alternate = an.BackupAnimal()
return handlePet(..., alternate, ...)
}
}
```
The above uses sentinel values, where the error must be equal (in the sense of
`==`) to the expected value. That is perfectly adequate in many cases. If
`process` returns wrapped errors (discussed below), you can use [`errors.Is`].
```go
// Good:
func handlePet(...) {
switch err := process(an); {
case errors.Is(err, ErrDuplicate):
return fmt.Errorf("feed %q: %v", an, err)
case errors.Is(err, ErrMarsupial):
// ...
}
}
```
Do not attempt to distinguish errors based on their string form. (See
[Go Tip #13: Designing Errors for Checking](https://google.github.io/styleguide/go/index.html#gotip)
for more.)
```go
// Bad:
func handlePet(...) {
err := process(an)
if regexp.MatchString(`duplicate`, err.Error()) {...}
if regexp.MatchString(`marsupial`, err.Error()) {...}
}
```
If there is extra information in the error that the caller needs
programmatically, it should ideally be presented structurally. For example, the
[`os.PathError`] type is documented to place the pathname of the failing
operation in a struct field which the caller can easily access.
Other error structures can be used as appropriate, for example a project struct
containing an error code and detail string. [Package `status`][status] is a
common encapsulation; if you choose this approach (which you are not obligated
to do), use [canonical codes]. See
[Go Tip #89: When to Use Canonical Status Codes as Errors](https://google.github.io/styleguide/go/index.html#gotip)
to know if using status codes is the right choice.
[`os.PathError`]: https://pkg.go.dev/os#PathError
[`errors.Is`]: https://pkg.go.dev/errors#Is
[`errors.As`]: https://pkg.go.dev/errors#As
[`package cmp`]: https://pkg.go.dev/github.com/google/go-cmp/cmp
[status]: https://pkg.go.dev/google.golang.org/grpc/status
[canonical codes]: https://pkg.go.dev/google.golang.org/grpc/codes
<a id="error-extra-info"></a>
### Adding information to errors
Any function returning an error should strive to make the error value useful.
Often, the function is in the middle of a callchain and is merely propagating an
error from some other function that it called (maybe even from another package).
Here there is an opportunity to annotate the error with extra information, but
the programmer should ensure there's sufficient information in the error without
adding duplicate or irrelevant detail. If you're unsure, try triggering the
error condition during development: that's a good way to assess what the
observers of the error (either humans or code) will end up with.
Convention and good documentation help. For example, the standard package `os`
advertises that its errors contain path information when it is available. This
is a useful style, because callers getting back an error don't need to annotate
it with information that they had already provided the failing function.
```go
// Good:
if err := os.Open("settings.txt"); err != nil {
return err
}
// Output:
//
// open settings.txt: no such file or directory
```
If there is something interesting to say about the *meaning* of the error, of
course it can be added. Just consider which level of the callchain is best
positioned to understand this meaning.
```go
// Good:
if err := os.Open("settings.txt"); err != nil {
// We convey the significance of this error to us. Note that the current
// function might perform more than one file operation that can fail, so
// these annotations can also serve to disambiguate to the caller what went
// wrong.
return fmt.Errorf("launch codes unavailable: %v", err)
}
// Output:
//
// launch codes unavailable: open settings.txt: no such file or directory
```
Contrast with the redundant information here:
```go
// Bad:
if err := os.Open("settings.txt"); err != nil {
return fmt.Errorf("could not open settings.txt: %w", err)
}
// Output:
//
// could not open settings.txt: open settings.txt: no such file or directory
```
When adding information to a propagated error, you can either wrap the error or
present a fresh error. Wrapping the error with the `%w` verb in `fmt.Errorf`
allows callers to access data from the original error. This can be very useful
at times, but in other cases these details are misleading or uninteresting to
the caller. See the
[blog post on error wrapping](https://blog.golang.org/go1.13-errors) for more
information. Wrapping errors also expands the API surface of your package in a
non-obvious way, and this can cause breakages if you change the implementation
details of your package.
It is best to avoid using `%w` unless you also document (and have tests that
validate) the underlying errors that you expose. If you do not expect your
caller to call `errors.Unwrap`, `errors.Is` and so on, don't bother with `%w`.
The same concept applies to [structured errors](#error-structure) like
[`*status.Status`][status] (see [canonical codes]). For example, if your server
sends malformed requests to a backend and receives an `InvalidArgument` code,
this code should *not* be propagated to the client, assuming that the client has
done nothing wrong. Instead, return an `Internal` canonical code to the client.
However, annotating errors helps automated logging systems preserve the status
payload of an error. For example, annotating the error is appropriate in an
internal function:
```go
// Good:
func (s *Server) internalFunction(ctx context.Context) error {
// ...
if err != nil {
return fmt.Errorf("couldn't find remote file: %w", err)
}
}
```
Code directly at system boundaries (typically RPC, IPC, storage, and similar)
should report errors using the canonical error space. It is the responsibility
of code here to handle domain-specific errors and represent them canonically.
For example:
```go
// Bad:
func (*FortuneTeller) SuggestFortune(context.Context, *pb.SuggestionRequest) (*pb.SuggestionResponse, error) {
// ...
if err != nil {
return nil, fmt.Errorf("couldn't find remote file: %w", err)
}
}
```
```go
// Good:
import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
func (*FortuneTeller) SuggestFortune(context.Context, *pb.SuggestionRequest) (*pb.SuggestionResponse, error) {
// ...
if err != nil {
// Or use fmt.Errorf with the %w verb if deliberately wrapping an
// error which the caller is meant to unwrap.
return nil, status.Errorf(codes.Internal, "couldn't find fortune database", status.ErrInternal)
}
}
```
See also:
* [Error Documentation Conventions](#documentation-conventions-errors)
<a id="error-percent-w"></a>
### Placement of %w in errors
Prefer to place `%w` at the end of an error string.
Errors can be wrapped with
[the `%w` verb](https://blog.golang.org/go1.13-errors), or by placing them in a
[structured error](https://google.github.io/styleguide/go/index.html#gotip) that
implements `Unwrap() error` (ex:
[`fs.PathError`](https://pkg.go.dev/io/fs#PathError)).