Google Go Style Guideを読んでみよう！ - Style Decisions編

## はじめに
みなさん、こんにちは！
Go大好きエンジニアの[くろたく](https://twitter.com/TakumaKurosawa)です。

ちょっと前になりますが、GoogleからGoのスタイルガイドが発表されましたね🙌

<blockquote class="twitter-tweet"><p lang="en" dir="ltr">A Go style guide from Google is dropped!<br><br>So many gems here. I read it all in one sitting.<br><br>It’s still in progress. Looking forward to the Go tips section.<a href="https://t.co/grav7zkJFJ">https://t.co/grav7zkJFJ</a><a href="https://twitter.com/hashtag/golang?src=hash&amp;ref_src=twsrc%5Etfw">#golang</a></p>&mdash; inanc (@inancgumus) <a href="https://twitter.com/inancgumus/status/1593939413372895232?ref_src=twsrc%5Etfw">November 19, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

今更ながら、アドカレの機会を利用してスタイルガイドを読んでアウトプットしようと考えました。

とはいえ、スタイルガイドの内容全てを読んで一気にアウトプットするとなると相当な時間と労力がかかります。そこで、この記事は1度アウトプットしたら終わりというものにするのではなく、書き足していくこと前提で書こうと決めました。
アップデートを見逃さないように、LGTM、ストックしておくことをオススメします👍

Go Style Guildeを読んでみようかなと思ってもらえるきっかけになれたら嬉しいです！
それでは、[CyberAgent AI tech studio | Go Advent Calendar 2022](https://qiita.com/advent-calendar/2022/ca-ai-go)の18日目の記事スタートです🎉


## 自分に合ったスタイルガイドを見つけよう！

Go Style Guideのトップページには、３種類の文書が存在することと、それぞれの文書にどんなことが書かれているかがまとめられています。


|文書  |対象読者  |書かれている内容  |
|---|---|---|
|[Style Guide](https://google.github.io/styleguide/go/guide)  |全ての人  |スタイルの原則(明瞭性、単純性、簡潔性、保守性、一貫性)について。それぞれの説明と事例を交えた深堀り。   |
|[Style Decisions](https://google.github.io/styleguide/go/decisions	)  |Goのレビュワーとなるメンター(Readability Mentors)  |スタイルの原則を達成するためにGoにおいて意識するべきコードの書き方をまとめたもの。  |
|[Best Practices](https://google.github.io/styleguide/go/best-practices)  |興味のある人  |Goで開発する際に出くわすことの多いコード例を取り上げ、各ケースごとにどのようにスタイルガイドを適用させていくべきか書かれている。  |


※この記事では、2番目のStyle Decisionsの内容をまとめようと思っています。
※comming soon...となっているセクションについては追記されていく想定です。
※優先順位については筆者の独断と偏見で、重要だと思ったところから順に書いています。
※このセクション優先的に書いてほしい！という要望があればコメント欄や[Twitter](https://twitter.com/TakumaKurosawa/status/1604408110709321728?s=20&t=zsnQMznmgnME_0uQqjK-Fg)にてお知らせください。


## Naming

### Underscores - アンダースコア（`_`）について

下記の例外を除いては基本的に使ってはだめ。

1. 自動生成されたコードのパッケージ名
1. テストコード（`*_test.go`）内の関数名
1. OSやcgoを制御するような低レイヤーライブラリにおける、識別子を再利用する場合


### Package names - package名について

**小文字のアルファベットのみで構成されている必要がある。**
例：
`tabwriter` → ⭕️
`tabWriter` → ❌
`TabWriter` → ❌
`tab_writer` → ❌

**よく使われるローカル変数名は避ける。**
例：
`count` → `usercount`

**情報量の多いパッケージ名は避ける。**
例：
`util`, `utility`, `common`, `helper`

▼ [Best practicesドキュメント](https://google.github.io/styleguide/go/best-practices#util-packages)より参照したutilパッケージの例

```go
// Good:
db := spannertest.NewDatabaseFromFile(...)
```

```go
// Bad:
db := test.NewDatabaseFromFile(...)
```

### Reciever names - Receiver名について

Receiver名は、

- 短く（だいたい1〜２文字くらいの長さが目安）
- 型名の略称
- レシーバーごとに名前が違うのはNG

であるべき。

|悪い例  |良い例  |
|---|---|
|`func (tray Tray)`  |`func (t Tray)`  |
|`func (info *ResearchInfo)`  |`func (ri *ResearchInfo)`  |
|`func (this *ReportWriter)`  |`func (w *ReportWriter)`  |
|`func (self *Scanner)`  |`func (s *Scanner)`  |

### Constant names - 定数名について

定数もGoの基本原則とされている[MixedCaps](https://google.github.io/styleguide/go/guide#mixed-caps)に従って、キャメルケースで書く。

大文字から始めるか小文字から始めるかで他のパッケージから使われるグローバル定数か、パッケージ内のみで利用できるローカル変数かを区別する。

```go
// Good:
const MaxPacketSize = 512

const (
    ExecuteBit = 1 << iota
    WriteBit
    ReadBit
)
```

```go
// Bad:
const MAX_PACKET_SIZE = 512
const kMaxBufferSize = 1024
const KMaxUsersPergroup = 500
```

定数には、値自体ではなく、役割に基づいて名前をつけること。
もしも、定義しようとしている定数がその値とは別の意味や役割を持たないのであれば、定数として定義する必要はない。

```go
// Bad:
const Twelve = 12

const (
    UserNameColumn = "username"
    GroupColumn    = "group"
)
```

### Initialisms - 頭字語(`URL`や`ID`)について

`URL` や `ID` などのように、各単語の頭文字からなる略である場合は、それらを同じケース（大文字なら全て大文字）で表す必要がある。

例：
❌ `URL` → `Url`
❌ `appID` → `appId`

下記を参考にするとわかりやすいと思います。

|Initialism(s) |	Scope |	Correct |	Incorrect |
|--|--|--|--|
XML API |	Exported |	XMLAPI	| XmlApi, XMLApi, XmlAPI, XMLapi|
XML API |	Unexported |	xmlAPI |	xmlapi, xmlApi
iOS |	Exported |	IOS |	Ios, IoS
iOS |	Unexported |	iOS |	ios
gRPC | 	Exported	| GRPC |	Grpc
gRPC |	Unexported |	gRPC |	grpc
DDoS |	Exported |	DDoS |	DDOS, Ddos
DDoS |	Unexported |	ddos |	dDoS, dDOS

### Getters

関数名に基本概念としてGetという単語が含まれていない限り`Get`を付与するべきでない。
※基本概念としてGetという単語が含まれている例： `HTTP GET`

```go
// Bad
func GetCounts() int

// Good
func Counts() int
```

関数が複雑な計算の実行やリモートコールを伴う場合は、Getの代わりに`Compute`や`Fetch`などの別の単語を使用する。目的としては、関数呼び出しに時間がかかり、ブロックや失敗する可能性があることを関数の使用者に伝える目的がある。

### Variable names - 変数名について

変数名の長さは、対象とするスコープの大きさに比例し、そのスコープ内で使用される回数に反比例するはず。
つまり、スコープが広くなるほど、変数名は長くなり、使用頻度が低ければ変数名は短くなるということ。

ファイルスコープで宣言された変数は複数の単語を必要とするかもしれないが、単一のインナーブロックにスコープされた変数はコードを明確に保ち、余計な情報を省略するために１単語または、1~2文字で済むかもしれない。

▼ ファイルスコープの例
```user.go
var (
    Users []*User = {
        {...}
    }
)
```

▼ 単一のインナーブロックスコープの例
```go
for i range users {
    fmt.Pringln(users[i])
}
```

スコープの範囲について、目安は下記。

- 小範囲：1～7行程度の小さな操作を1～2回行うもの
- 中程度のスコープ：8～15行程度の小さな操作を数回、または大きな操作を1回行うもの
- 大きなスコープ：1つまたはいくつかの大きな操作が行われるもので、例えば15～25行のもの
- 非常に大きな範囲：1ページ以上にまたがるもの（例えば25行以上）

他の判断軸として、概念の具体性がある。
例えば、使用するデータベースが１つだけだと仮定すると、`db`のような短い変数名は、スコープが非常に大きい場合でも明確なままである可能性が高い。
このように、変数名が短くとも、その変数が表す概念が一意に定まる場合であれば短い変数名も許容される。

ローカル変数の名前は、その値がどこで作られた火よりも、その変数が何を含んでいて、現在の文脈でどのように使われているかを反映するべき。という前提を起きつつ、下記に一般的なアプローチを示す。

- 一語の変数名はファーストチョイスとして良い場合が多い（例：`count`, `options`）
- 似たような変数の違いについて、単語を追加することで曖昧さをなくすことができる（例：`userCount`, `prodjectCount`）
- 短い変数名が良いからといって、単純に文字を削除して略語化することは避けること。（NG：`Sandbox` → `sbx`）
- 変数名には型に関する情報を省くこと
    - ❌ `numUsers`,`usersInt` → ⭕️ `userCount`
    - ❌ `userSlice` → ⭕️ `users`
    - 例外として、入力値としてはstring型で受け取った`age`をintへパースする場合などについては、inputの変数名として`ageString`などを使うことは許容される
- 周囲の文脈から明らかな単語は省略する。
    - 例えば、UserCountメソッドの実装では、userCountというローカル変数はおそらく冗長である。

1文字の変数名について、下記のような場合に使うと良い。

- メソッドのレシーバー変数には、1文字または2文字の名前を付けるのが好ましい。
- 一般的な型のために馴染みのある変数名を使う場合
    - `r` は `io.Reader` や `*http.Request` を表します。
    - `w` は `io.Writer` または `http.ResponseWriter` です。
- 整数型のループ変数では、特にインデックス（iなど）や座標（xやyなど）には一文字の識別子を使うことができる
- スコープが短い場合は、`for _, n := range nodes { ... }`のように省略形もループ識別子として使用できる。

### Repetition - 変数の冗長化に対する対策

変数名が長くなりすぎないように、不必要に文脈や型名を繰り返すのは避けましょう。

**Package vs. exported symbol name**

- ❌ `widget.NewWidget` → ⭕️ `widget.New`
- ❌ `widget.NewWidgetWithName` → ⭕️ `widget.NewWithName`
- ❌ `db.LoadFromDatabase` → ⭕️ `db.Load`
- ❌ `goatteleportutil.CountGoatsTeleported` → ⭕️ `gtutil.CountGoatsTeleported` or `goatteleport.Count`
- ❌ `myteampb.MyTeamMethodRequest` → ⭕️ `mtpb.MyTeamMethodRequest` or `myteampb.MethodRequest`

**Variable name vs. type**

- ❌ `var numUsers int` → ⭕️ `var users int`
- ❌ `var nameString int` → ⭕️ `var name string`
- ❌ `var primaryProject int` → ⭕️ `var primary *Project`

パースすることが前提になっているユーザーからの入力値などの変数名では型名を変数名に入れて良いこととする。

```go
// Good
limitStr := r.FormValue("limit")
limit, err := strconv.Atoi(limitStr)
```

**External context vs. local names**

名前に周囲の文脈の情報を含めると、多くの場合余計なノイズになる。
パッケージ名、メソッド名、型名、関数名、インポートパス、そしてファイル名さえも変数名からは省略するべきでしょう。


▼　In package "ads/targeting/revenue/reporting"
```go
// Bad:
type AdsTargetingRevenueReport struct{}

func (p *Project) ProjectName() string
```

```go
// Good:
type Report struct{}

func (p *Project) Name() string
```

▼ In package "sqldb"
```go
// Bad:
type DBConnection struct{}
```

```go
// Good:
type Connection struct{}
```

▼ In package "ads/targeting/revenue/reporting"
```go
// Bad:
func Process(in *pb.FooProto) *Report {
    adsTargetingID := in.GetAdsTargetingID()
}
```

```go
// Good:
func Process(in *pb.FooProto) *Report {
    id := in.GetAdsTargetingID()
}
```


文脈や用法から明らかな名称についてもできるだけ省略するようにしよう。

```go
// Bad:
func (db *DB) UserCount() (userCount int, err error) {
    var userCountInt64 int64
    if dbLoadError := db.LoadFromDatabase("count(distinct users)", &userCountInt64); dbLoadError != nil {
        return 0, fmt.Errorf("failed to load user count: %s", dbLoadError)
    }
    userCount = int(userCountInt64)
    return userCount, nil
}
```

```go
// Good:
func (db *DB) UserCount() (int, error) {
    var count int64
    if err := db.Load("count(distinct users)", &count); err != nil {
        return 0, fmt.Errorf("failed to load user count: %s", err)
    }
    return int(count), nil
}
```

## Commentary

### Comment line length - コメントの文字数について

comming soon...

### Doc comments - ドキュメント用コメントについて

comming soon...

### Comment sentences - コメント文について

comming soon...

### Examples - 例

comming soon...

### Named result parameters - 名前付き結果パラメータについて

comming soon...

### Package comments - パッケージの説明文について

comming soon...


## Imports

### Import renaming - インポートのエイリアス化について

comming soon...

### Import grouping - インポートのグループ化について

comming soon...

### Import "blank"（`import _`） - blankインポートについて

comming soon...

### Import "dot"(`import .`) - dotインポートについて

comming soon...


## Errors

### Returning errors - エラーのreturnについて

関数が失敗する可能性を含んでいる場合、返り値に`error`を指定する。
慣習上、`error`は最後の返り値に指定されることが多いので、それに従う。

```go
// Good:
func Good() error { /* ... */ }
```

`error`の返り値として`nil`を返すことは、失敗する可能性のある処理に成功したことを知らせる一般的な方法である。
もしエラーが発生して`err != nil`の状態になった場合は、呼び出し側でエラー以外の戻り値を未指定として扱う必要がある。

```go
// Good:
func GoodLookup() (*Result, error) {
    res, err := doSomeThing()
    if err != nil {
        return nil, err
    }
    return res, nil
}
```

エラーを返す関数は、基本的に`error`型を利用するようにしましょう。

```go
// Bad:
func Bad() *os.PathError { /*...*/ }
```

### Error strings - エラー文言について

エラー文言は、固有名詞や頭字語で始まらない限りは大文字から始めてはいけない。
また、文章のように句読点で終わらせてはいけない。

```go
// Bad:
err := fmt.Errorf("Something bad happened.")
```

```go
// Good:
err := fmt.Errorf("something bad happened")
```

一方、エラーメッセージ（ロギング、テスト失敗、APIレスポンス、その他のUI上で表示されるメッセージなど）では、大文字から始まる必要がある。

```go
// Good:
log.Infof("Operation aborted: %v", err)
log.Errorf("Operation aborted: %v", err)
t.Errorf("Op(%q) failed unexpectedly; err=%v", args, err)
```

### Handle errors - エラーハンドリングについて

エラーに遭遇したコードは、エラーをどのように処理するか意図的に選択する必要がある。
通常、`_`変数を利用してエラーを無視することは得策とは言えない。
関数がエラーを返した場合は、以下のいずれかを実行する必要がある。

- エラーを直ちに処理して対処する。
- エラーを呼び出し元に返す。
- 例外的な状況においては、`log.Fatal`または`panic`を呼び出す。

ライブラリによりエラーが起きることがないことを保証されている場合（ `(*bytes.Buffer).Write`など）の、エラーを無視できる稀な状況では、付随するコメントでなぜそれが安全なのかを説明する必要がある。

```go
// Good:
var b *bytes.Buffer

n, _ := b.Write(p) // never returns a non-nil error
```

### In-band errors - OSにおけるexit codeについて

comming soon...

### Indent error flow - エラー処理と正常処理の書き方について

関数を呼び出した後は、必ずエラー処理をしてからコードを進めるようにすること。
これにより、読み手が正常なパスを素早く見つける手助けになる。

エラーが起きない場合の正常なパスで実行されるコードは、ifブロックの後に表示されるべきで、else節のなかでインデントされるべきではない。

```go
// Good:
if err != nil {
    // error handling
    return // or continue, etc.
}
// normal code
```

```go
// Bad:
if err != nil {
    // error handling
} else {
    // normal code that looks abnormal due to indentation
}
```

関数の返り値を後の処理でも利用したい場合は、if文のブロックスコープに変数を閉じ込めてしまう書き方は避けるべきでしょう。

```go
// Good:
x, err := f()
if err != nil {
  // error handling
  return
}
// lots of code that uses x
// across multiple lines
```

```go
// Bad:
if x, err := f(); err != nil {
  // error handling
  return
} else {
  // lots of code that uses x
  // across multiple lines
}
```

## Language

### Literal formatting - リテラル構文について

Goには強力な複合リテラル構文があり、深くネストした複雑な値を１つの式で表現することができる。
可能な限りフィールドごとに値を構築するのではなく、リテラル構文を使用しましょう。
リテラルをより読みやすくするための追加ルールを下記に示す。

**Field names**

構造体への代入をする時は、フィールド名を指定して代入しよう。

```go
// Good:
good := otherpkg.Type{A: 42}
```

```go
// Bad:
// https://pkg.go.dev/encoding/csv#Reader
r := csv.Reader{',', '#', 4, false, false, false, false}
```

**Matcing braces**

構造体の括弧`{}`のインデントは揃えよう。

```go
// Good:
good := []*Type{{Key: "value"}}
```

```go
// Good:
good := []*Type{
    {Key: "multi"},
    {Key: "line"},
}
```

```go
// Bad:
bad := []*Type{
    {Key: "multi"},
    {Key: "line"}}
```

```go
// Bad:
bad := []*Type{
    {
        Key: "value"},
}
```

**Cuddled braces**

スライスや配列の構造体で中括弧の間の括弧を削除する場合は下記の場合のみに限る。

- インデントが揃っている場合
- 内部の値（フィールド）もリテラルやプロトビルダーであること（変数や他の式ではないこと）

```go
// Good:
good := []*Type{
    { // Not cuddled
        Field: "value",
    },
    {
        Field: "value",
    },
}
```

```go
// Good:
good := []*Type{{ // Cuddled correctly
    Field: "value",
}, {
    Field: "value",
}}
```

```go
// Good:
good := []*Type{
    first, // Can't be cuddled
    {Field: "second"},
}
```

```go
// Good:
okay := []*pb.Type{pb.Type_builder{
    Field: "first", // Proto Builders may be cuddled to save vertical space
}.Build(), pb.Type_builder{
    Field: "second",
}.Build()}
```

```go
// Bad:
bad := []*Type{
    first,
    {
        Field: "second",
    }}
```

**Repeated type names**

スライスやマップの構造体定義において、繰り返される型名宣言は省略しよう。
型名を明示的に示した方が良い場合は、プロジェクトでは一般的ではない複雑な型を扱うときや、繰り返される型名が離れた行にあり、読み手に文脈を思い出させる必要がある時のみ。

```go
// Good:
good := []*Type{
    {A: 42},
    {A: 43},
}
```

```go
// Bad:
repetitive := []*Type{
    &Type{A: 42},
    &Type{A: 43},
}
```

```go
// Good:
good := map[Type1]*Type2{
    {A: 1}: {B: 2},
    {A: 3}: {B: 4},
}
```

```go
// Bad:
repetitive := map[Type1]*Type2{
    Type1{A: 1}: &Type2{B: 2},
    Type1{A: 3}: &Type2{B: 4},
}
```

💡Tips: 上記のルールに従って、繰り返される型名を削除したい場合は、`gofmt -s`を実行すると良いでしょう。

**Zero-value fields**

ゼロ値を代入したいフィールドは、構造体リテラルの明確性が損なわれない場合においては省略することができる。
ゼロ値フィールドの省略により、代入されるフィールドのみに注目が集まるようになる。

```go
// Bad:
import (
  "github.com/golang/leveldb"
  "github.com/golang/leveldb/db"
)

ldb := leveldb.Open("/my/table", &db.Options{
    BlockSize: 1<<16,
    ErrorIfDBExists: true,

    // These fields all have their zero values.
    BlockRestartInterval: 0,
    Comparer: nil,
    Compression: nil,
    FileSystem: nil,
    FilterPolicy: nil,
    MaxOpenFiles: 0,
    WriteBufferSize: 0,
    VerifyChecksums: false,
})
```

```go
// Good:
import (
  "github.com/golang/leveldb"
  "github.com/golang/leveldb/db"
)

ldb := leveldb.Open("/my/table", &db.Options{
    BlockSize: 1<<16,
    ErrorIfDBExists: true,
})
```

### Nil slices - nil値を返すsliceについて

`nil`と空のスライスの間に機能的な違いは存在しない。
`len`や`cap`のような組み込み関数はnilスライスでも期待通りに動作する。

```go
// Good:
import "fmt"

var s []int         // nil

fmt.Println(s)      // []
fmt.Println(len(s)) // 0
fmt.Println(cap(s)) // 0
for range s {...}   // no-operation

s = append(s, 42)
fmt.Println(s)      // [42]
```

空のスライスをローカル変数として宣言する場合（特に、戻り値になる予定の場合）は、呼び出し側によるバグのリスクを減らすために`nil`の初期化を優先するようにすること。

```go
// Good:
var t []string
```

```go
// Bad:
t := []string{}
```

`nil`なのか、空のスライスなのかをクライアントに判別させるようなAPIは実装しないこと。

```go
// Good:
// Ping pings its targets.
// Returns hosts that successfully responded.
func Ping(hosts []string) ([]string, error) { ... }
```

```go
// Bad:
// Ping pings its targets and returns a list of hosts
// that successfully responded. Can be empty if the input was empty.
// nil signifies that a system error occurred.
func Ping(hosts []string) []string { ... }
```

スライスのチェックには、`len()`を使って比較すること。
空っぽかどうかを`== nil`でチェックすることは避けましょう。

```go
// Good:
// describeInts describes s with the given prefix, unless s is empty.
func describeInts(prefix string, s []int) {
    if len(s) == 0 {
        return
    }
    fmt.Println(prefix, s)
}
```

```go
// Bad:
func maybeInts() []int { /* ... */ }

// describeInts describes s with the given prefix; pass nil to skip completely.
func describeInts(prefix string, s []int) {
  // The behavior of this function unintentionally changes depending on what
  // maybeInts() returns in 'empty' cases (nil or []int{}).
  if s == nil {
    return
  }
  fmt.Println(prefix, s)
}

describeInts("Here are some ints:", maybeInts())
```

【FYI】
なぜ、空スライスかどうかのチェックを`== nil`で行ってはいけないかについては、下記の記事が参考になると思います。
[[Go]なぜsliceの空チェックで「nil」ではなく「長さ」でチェックするのか
](https://qiita.com/Yashy/items/1d4f277998866b186e19)



### Indentation confusion - インデントが招く混乱について

`if`や`for`などのコードブロック内の記述と条件式などの行が同一にならないようにしよう。

```go
// Bad:
if longCondition1 && longCondition2 &&
    // Conditions 3 and 4 have the same indentation as the code within the if.
    longCondition3 && longCondition4 {
    log.Info("all conditions met")
}
```

### Function formatting - 関数のフォーマットについて

関数についても、Indentation confusionで述べたように、引数が長い場合であっても１行で記述した方が良いでしょう。
インデントが変わることにより、ブロック内の処理と区別することが難しくなる。

```go
// Bad:
func (r *SomeType) SomeLongFunctionName(foo1, foo2, foo3 string,
    foo4, foo5, foo6 int) {
    foo7 := bar(foo1)
    // ...
}
```

大量の引数を関数に指定したい場合に使える、引数を短くするテクニックは、[Best practices](https://google.github.io/styleguide/go/best-practices#function-argument-lists)を参考にすると良いでしょう。
以下、Best practicesより引用した2つの回避策

**Option structure**
引数を１つの構造体にまとめてしまえば良いよね！という発想。

```go
// Bad:
func EnableReplication(ctx context.Context, config *replicator.Config, primaryRegions, readonlyRegions []string, replicateExisting, overwritePolicies bool, replicationInterval time.Duration, copyWorkers int, healthWatcher health.Watcher) {
    // ...
}
```

```go
// Good:
type ReplicationOptions struct {
    Config              *replicator.Config
    PrimaryRegions      []string
    ReadonlyRegions     []string
    ReplicateExisting   bool
    OverwritePolicies   bool
    ReplicationInterval time.Duration
    CopyWorkers         int
    HealthWatcher       health.Watcher
}

func EnableReplication(ctx context.Context, opts ReplicationOptions) {
    // ...
}
```

**Variadic options**
いわゆる可変長引数の実装。
注意書きでも書かれているが、コードの記述量は増えてしまうため、可変長引数を導入するメリットがオーバーヘッドを上回る場合のみ効果的とされている。

```go
// Bad:
func EnableReplication(ctx context.Context, config *placer.Config, primaryCells, readonlyCells []string, replicateExisting, overwritePolicies bool, replicationInterval time.Duration, copyWorkers int, healthWatcher health.Watcher) {
  ...
}
```

```go
// Good:
type replicationOptions struct {
    readonlyCells       []string
    replicateExisting   bool
    overwritePolicies   bool
    replicationInterval time.Duration
    copyWorkers         int
    healthWatcher       health.Watcher
}

// A ReplicationOption configures EnableReplication.
type ReplicationOption func(*replicationOptions)

// ReadonlyCells adds additional cells that should additionally
// contain read-only replicas of the data.
//
// Passing this option multiple times will add additional
// read-only cells.
//
// Default: none
func ReadonlyCells(cells ...string) ReplicationOption {
    return func(opts *replicationOptions) {
        opts.readonlyCells = append(opts.readonlyCells, cells...)
    }
}

// ReplicateExisting controls whether files that already exist in the
// primary cells will be replicated.  Otherwise, only newly-added
// files will be candidates for replication.
//
// Passing this option again will overwrite earlier values.
//
// Default: false
func ReplicateExisting(enabled bool) ReplicationOption {
    return func(opts *replicationOptions) {
        opts.replicateExisting = enabled
    }
}

// ... other options ...

// DefaultReplicationOptions control the default values before
// applying options passed to EnableReplication.
var DefaultReplicationOptions = []ReplicationOption{
    OverwritePolicies(true),
    ReplicationInterval(12 * time.Hour),
    CopyWorkers(10),
}

func EnableReplication(ctx context.Context, config *placer.Config, primaryCells []string, opts ...ReplicationOption) {
    var options replicationOptions
    for _, opt := range DefaultReplicationOptions {
        opt(&options)
    }
    for _, opt := range opts {
        opt(&options)
    }
}

// 　呼び出し側
func foo(ctx context.Context) {
    // Complex call:
    storage.EnableReplication(ctx, config, []string{"po", "is", "ea"},
        storage.ReadonlyCells("ix", "gg"),
        storage.OverwritePolicies(true),
        storage.ReplicationInterval(1*time.Hour),
        storage.CopyWorkers(100),
        storage.HealthWatcher(watcher),
    )

    // Simple call:
    storage.EnableReplication(ctx, config, []string{"po", "is", "ea"})
}
```

### Conditionals and loops - `if`と`for`について

`if`文の条件式に改行は入れるべきではない。（Indentation confusionでも述べた通り。）

```go
// Bad:
// The second if statement is aligned with the code within the if block, causing
// indentation confusion.
if db.CurrentStatusIs(db.InTransaction) &&
    db.ValuesEqual(db.TransactionKey(), row.Key()) {
    return db.Errorf(db.TransactionError, "query failed: row (%v): key does not match transaction key", row)
}
```

より短いコードで書かなければならないという制約がない場合であれば、下記のようにBool値の取得処理を切り出せば良い。

```go
// Good:
inTransaction := db.CurrentStatusIs(db.InTransaction)
keysMatch := db.ValuesEqual(db.TransactionKey(), row.Key())
if inTransaction && keysMatch {
    return db.Error(db.TransactionError, "query failed: row (%v): key does not match transaction key", row)
}
```

また、事前にデータを抽出しておくことで条件式を短くすることも可能だったりする。

```go
// Bad:
if db.UserIsAdmin(user.GetUniqueUserID()) || db.UserHasPermission(user.GetUniqueUserID(), perms.ViewServerConfig) || db.UserHasPermission(user.GetUniqueUserID(), perms.CreateGroup) {
    // ...
}
```

```go
// Good:
uid := user.GetUniqueUserID()
if db.UserIsAdmin(uid) || db.UserHasPermission(uid, perms.ViewServerConfig) || db.UserHasPermission(uid, perms.CreateGroup) {
    // ...
}
```

`if`のコードブロックの可読性を上げるために、`{}`のインデントを揃えたりはきちんと対応しましょう。

```go
// Good:
if err := db.RunInTransaction(func(tx *db.TX) error {
    return tx.Execute(userUpdate, x, y, z)
}); err != nil {
    return fmt.Errorf("user update failed: %s", err)
}

// Good:
if _, err := client.Update(ctx, &upb.UserUpdateRequest{
    ID:   userID,
    User: user,
}); err != nil {
    return fmt.Errorf("user update failed: %s", err)
}
```

`for`文については、なるべく改行を入れないようにしましょう。

```go
// Good:
for i, max := 0, collection.Size(); i < max && !collection.HasPendingWriters(); i++ {
    // ...
}
```

`switch`文についても、改行を入れないようにしましょう。

```go
// Good:
switch good := db.TransactionStatus(); good {
case db.TransactionStarting, db.TransactionActive, db.TransactionWaiting:
    // ...
case db.TransactionCommitted, db.NoTransaction:
    // ...
default:
    // ...
}
```

```go
// Bad:
switch bad := db.TransactionStatus(); bad {
case db.TransactionStarting,
    db.TransactionActive,
    db.TransactionWaiting:
    // ...
case db.TransactionCommitted,
    db.NoTransaction:
    // ...
default:
    // ...
}
```

`case`条件が長すぎる場合は、全ての条件式を改行した上で、コードブロック内の処理は１行空けてから記述すると良いでしょう。

```go
// Good:
switch db.TransactionStatus() {
case
    db.TransactionStarting,
    db.TransactionActive,
    db.TransactionWaiting,
    db.TransactionCommitted:

    // ...
case db.NoTransaction:
    // ...
default:
    // ...
}
```

`if`文の条件式については、比較演算子(`==`や`<=`)の左辺に変数を持ってきましょう。

```go
// Good:
if result == "foo" {
  // ...
}
```

```go
// Bad:
if "foo" == result {
  // ...
}
```

### Copying - 値のコピーについて

予期せぬエイリアスを生んだり、バグの原因になったりするため、他のパッケージから構造体をコピーする際は注意が必要。

- `sync.Mutex`のような同期オブジェクトはコピーNG
- `bytes.Buffer`型も、コピーすると内部で保持しているsliceのエイリアスが作られてしまい、副作用をもたらす可能性がある。

```go
// Bad:
mu := sync.Mutex{}
mu2 := mu
```

```go
// Bad:
b1 := bytes.Buffer{}
b2 := b1
```

レシーバーに指定する構造体のフィールドに、コピーされては困るフィールドが含まれている場合、一般的にポインタレシーバーにするべきである。

```go
// Good:
type Record struct {
  buf bytes.Buffer
  // other fields omitted
}

func New() *Record {...}

func (r *Record) Process(...) {...}

func Consumer(r *Record) {...}
```

下記のように、値レシーバーにしてしまうと、`r.buf`フィールドがコピーされる結果になってしまう。

```go
// Bad:
type Record struct {
  buf bytes.Buffer
  // other fields omitted
}

func (r Record) Process(...) {...} // Makes a copy of r.buf

func Consumer(r Record) {...} // Makes a copy of r.buf
```

### Don't panic - パニックはNG

`panic()`を使わず、通常のエラーハンドリングをするか、`return err`しましょう。
mainパッケージ内で、プログラムを強制終了させなければならないような、状況になった場合（configが間違っている場合など）は、`log.Exit()`を使ってプログラムを終了させましょう。

### Must functions - 失敗時にプログラムを停止させる関数について

失敗したらプログラム自体を終了させた方が良いような、セットアップヘルパー関数には、`must`という単語をつけましょう。また、このような関数はユーザーの入力値エラーなどをハンドリングするようなケースではなく、必ずプログラムが立ち上がるタイミングで実行されるようにしましょう。

```go
// Good:
func MustParse(version string) *Version {
    v, err := Parse(version)
    if err != nil {
        log.Fatalf("MustParse(%q) = _, %v", version, err)
    }
    return v
}

// Package level "constant". If we wanted to use `Parse`, we would have had to
// set the value in `init`.
var DefaultVersion = MustParse("1.2.3")
```

テーブル駆動テストのように、正解値である構造体を作る際などでも活用することが可能である。

```go
// Good:
func mustMarshalAny(t *testing.T, m proto.Message) *anypb.Any {
  t.Helper()
  any, err := anypb.New(m)
  if err != nil {
    t.Fatalf("MustMarshalAny(t, m) = %v; want %v", err, nil)
  }
  return any
}

func TestCreateObject(t *testing.T) {
  tests := []struct{
    desc string
    data *anypb.Any
  }{
    {
      desc: "my test case",
      // Creating values directly within table driven test cases.
      data: mustMarshalAny(t, mypb.Object{}),
    },
    // ...
  }
  // ...
}
```

プログラムを終了させるような可能性のある関数は、基本的に下記の条件に当てはまるような関数では導入しない方が良い。

- エラーを確実に捕捉するのが難しい
- エラーをチェックすべきコンテキスト（例：リクエストハンドラの実装箇所など）
- 通常のエラーハンドリングが可能な場所

```go
// Bad:
func Version(o *servicepb.Object) (*version.Version, error) {
    // Return error instead of using Must functions.
    v := version.MustParse(o.GetVersionString())
    return dealiasVersion(v)
}
```


### Goroutine lifetimes - Goroutineのライフタイムについて

comming soon...

### Interfaces - Interfaceについて

comming soon...

### Generics - Genericsについて

comming soon...

### Pass values - 値渡しについて

comming soon...

### Receiver type - レシーバーに指定する型について

comming soon...

### `Switch` and `break` - Switch文について

comming soon...

### Synchronous functions - 非同期関数について

comming soon...

### Type aliases - type aliasについて

comming soon...

### Use %q - `fmt.Printf`における`%q`について

comming soon...

### Use any - any型について

comming soon...


## Common libraries

### Flags

comming soon...

### Logging

comming soon...

### Contexts

comming soon...

### crypto/rand

comming soon...



## Useful test failures

### Assertion libraries - アサーションライブラリについて

comming soon...

### Identify the function - テスト対象関数の特定について

comming soon...

### Identify the input - エラーの原因となったinputの特定について

comming soon...

### Got before want - 実際に得られた結果は先に表示せよ

comming soon...

### Full structure comparisons - 構造体の比較について

comming soon...

### Compare stable results - 安定した結果の比較について

comming soon...

### Keep going - 失敗しても突き進め

comming soon...

### Equality comparison and diffs - 等価比較と差分について

comming soon...

### Level of detail - 詳細のレベルについて

comming soon...

### Print diffs - 比較結果のプリント方法について

comming soon...

### Test error semantics - 関数の返すエラーの種類チェックについて

comming soon...


## Test structure

### Subtests - サブテストについて

comming soon...

### Table-driven tests - テーブル駆動テストについて

comming soon...

### Test helpers - テストヘルパー関数について

comming soon...

### Test package - テストパッケージについて

comming soon...

### Use package `testing` - `testing`ライブラリを使いましょう

comming soon...


## Non-decisions