292 lines
12 KiB
Markdown
292 lines
12 KiB
Markdown
---
|
||
description: '慣用的なGoプラクティスとコミュニティ標準に従ったGoコード記述の指針'
|
||
applyTo: '**/*.go,**/go.mod,**/go.sum'
|
||
---
|
||
|
||
# Go開発指針
|
||
|
||
Goコードを書く際は慣用的なGoプラクティスとコミュニティ標準に従ってください。これらの指針は[Effective Go](https://go.dev/doc/effective_go)、[Go Code Review Comments](https://go.dev/wiki/CodeReviewComments)、[GoogleのGoスタイルガイド](https://google.github.io/styleguide/go/)に基づいています。
|
||
|
||
## 全般指針
|
||
|
||
- シンプルで明確、慣用的なGoコードを記述
|
||
- 巧妙さよりも明確性とシンプルさを優先
|
||
- 最小驚きの原則に従う
|
||
- ハッピーパスを左寄せにする(インデントを最小化)
|
||
- ネストを減らすため早期リターンを使用
|
||
- ゼロ値を有用にする
|
||
- エクスポートされた型、関数、メソッド、パッケージを文書化
|
||
- 依存関係管理にはGoモジュールを使用
|
||
|
||
## 命名規則
|
||
|
||
### パッケージ
|
||
|
||
- 小文字の単語でパッケージ名を使用
|
||
- アンダースコア、ハイフン、mixedCapsを避ける
|
||
- パッケージが何を含むかではなく、何を提供するかを説明する名前を選択
|
||
- `util`、`common`、`base`などの汎用的な名前を避ける
|
||
- パッケージ名は複数形ではなく単数形にする
|
||
|
||
### 変数と関数
|
||
|
||
- アンダースコアではなくmixedCapsまたはMixedCaps(camelCase)を使用
|
||
- 短いが説明的な名前を維持
|
||
- 単文字変数は非常に短いスコープ(ループインデックスなど)でのみ使用
|
||
- エクスポートされた名前は大文字で始まる
|
||
- エクスポートされていない名前は小文字で始まる
|
||
- 重複を避ける(例:`http.HTTPServer`を避け、`http.Server`を使用)
|
||
|
||
### インターフェース
|
||
|
||
- 可能な場合はインターフェース名に-er接尾辞を使用(例:`Reader`、`Writer`、`Formatter`)
|
||
- 単一メソッドインターフェースはメソッド名で命名(例:`Read` → `Reader`)
|
||
- インターフェースは小さく焦点を絞って維持
|
||
|
||
### 定数
|
||
|
||
- エクスポートされた定数にはMixedCapsを使用
|
||
- エクスポートされていない定数にはmixedCapsを使用
|
||
- 関連する定数は`const`ブロックでグループ化
|
||
- より良い型安全性のため型付き定数を検討
|
||
|
||
## コードスタイルとフォーマット
|
||
|
||
### フォーマット
|
||
|
||
- コードのフォーマットには常に`gofmt`を使用
|
||
- インポートの自動管理には`goimports`を使用
|
||
- 行の長さを適度に保つ(厳格な制限はないが、可読性を考慮)
|
||
- 論理的なコードグループを分離するため空行を追加
|
||
|
||
### コメント
|
||
|
||
- 完全な文でコメントを記述
|
||
- 説明される対象の名前で文を開始
|
||
- パッケージコメントは「Package [name]」で開始
|
||
- ほとんどのコメントには行コメント(`//`)を使用
|
||
- ブロックコメント(`/* */`)は控えめに使用、主にパッケージ文書化用
|
||
- 何をするかではなく、なぜするかを文書化(何が複雑でない限り)
|
||
|
||
### エラーハンドリング
|
||
|
||
- 関数呼び出しの直後にエラーをチェック
|
||
- 良い理由がない限り`_`を使ってエラーを無視しない(理由を文書化)
|
||
- `fmt.Errorf`と`%w`動詞を使用してコンテキストでエラーをラップ
|
||
- 特定のエラーをチェックする必要がある場合はカスタムエラー型を作成
|
||
- エラー戻り値を最後の戻り値として配置
|
||
- エラー変数は`err`と命名
|
||
- エラーメッセージは小文字で、句読点で終わらない
|
||
|
||
## アーキテクチャとプロジェクト構造
|
||
|
||
### パッケージ構成
|
||
|
||
- 標準Go プロジェクトレイアウト規則に従う
|
||
- `main`パッケージは`cmd/`ディレクトリに保持
|
||
- 再利用可能なパッケージは`pkg/`または`internal/`に配置
|
||
- 外部プロジェクトにインポートされるべきでないパッケージには`internal/`を使用
|
||
- 関連機能をパッケージにグループ化
|
||
- 循環依存関係を避ける
|
||
|
||
### 依存関係管理
|
||
|
||
- Goモジュール(`go.mod`と`go.sum`)を使用
|
||
- 依存関係を最小限に保つ
|
||
- セキュリティパッチのため定期的に依存関係を更新
|
||
- `go mod tidy`を使用して未使用の依存関係をクリーンアップ
|
||
- 必要な場合のみ依存関係をベンダー化
|
||
|
||
## 型安全性と言語機能
|
||
|
||
### 型定義
|
||
|
||
- 意味と型安全性を追加するため型を定義
|
||
- JSON、XML、データベースマッピングには構造体タグを使用
|
||
- 明示的な型変換を優先
|
||
- 型アサーションは慎重に使用し、第二戻り値をチェック
|
||
|
||
### ポインターvs値
|
||
|
||
- 大きな構造体またはレシーバーを変更する必要がある場合はポインターを使用
|
||
- 小さな構造体とイミュータビリティが望ましい場合は値を使用
|
||
- 型のメソッドセット内で一貫性を保つ
|
||
- ポインターvs値レシーバーを選択する際はゼロ値を考慮
|
||
|
||
### インターフェースと合成
|
||
|
||
- インターフェースを受け入れ、具象型を返す
|
||
- インターフェースを小さく保つ(1〜3メソッドが理想的)
|
||
- 合成には埋め込みを使用
|
||
- 実装される場所ではなく使用される場所の近くでインターフェースを定義
|
||
- 必要でない限りインターフェースをエクスポートしない
|
||
|
||
## 並行性
|
||
|
||
### ゴルーチン
|
||
|
||
- ライブラリでゴルーチンを作成しない;呼び出し元に並行性を制御させる
|
||
- ゴルーチンがどのように終了するかを常に知る
|
||
- ゴルーチンを待つために`sync.WaitGroup`またはチャネルを使用
|
||
- クリーンアップを確実に行うことでゴルーチンリークを避ける
|
||
|
||
### チャネル
|
||
|
||
- ゴルーチン間の通信にはチャネルを使用
|
||
- メモリを共有して通信するのではなく、通信してメモリを共有する
|
||
- 受信側ではなく送信側からチャネルを閉じる
|
||
- 容量が分かっている場合はバッファードチャネルを使用
|
||
- 非ブロッキング操作には`select`を使用
|
||
|
||
### 同期
|
||
|
||
- 共有状態の保護には`sync.Mutex`を使用
|
||
- クリティカルセクションを小さく保つ
|
||
- 多くの読み手がいる場合は`sync.RWMutex`を使用
|
||
- 可能な場合はミューテックスよりもチャネルを優先
|
||
- 一度だけの初期化には`sync.Once`を使用
|
||
|
||
## エラーハンドリングパターン
|
||
|
||
### エラー作成
|
||
|
||
- シンプルな静的エラーには`errors.New`を使用
|
||
- 動的エラーには`fmt.Errorf`を使用
|
||
- ドメイン固有のエラーにはカスタムエラー型を作成
|
||
- センチネルエラーにはエラー変数をエクスポート
|
||
- エラーチェックには`errors.Is`と`errors.As`を使用
|
||
|
||
### エラー伝播
|
||
|
||
- スタックを上に伝播する際にコンテキストを追加
|
||
- エラーをログして返す(どちらか一方を選択)
|
||
- 適切なレベルでエラーを処理
|
||
- より良いデバッグのため構造化エラーの使用を検討
|
||
|
||
## API設計
|
||
|
||
### HTTPハンドラー
|
||
|
||
- シンプルなハンドラーには`http.HandlerFunc`を使用
|
||
- 状態が必要なハンドラーには`http.Handler`を実装
|
||
- 横断的関心事にはミドルウェアを使用
|
||
- 適切なステータスコードとヘッダーを設定
|
||
- エラーを適切に処理し、適切なエラーレスポンスを返す
|
||
|
||
### JSON API
|
||
|
||
- JSONマーシャリングを制御するため構造体タグを使用
|
||
- 入力データを検証
|
||
- オプションフィールドにはポインターを使用
|
||
- 遅延解析のため`json.RawMessage`の使用を検討
|
||
- JSONエラーを適切に処理
|
||
|
||
## パフォーマンス最適化
|
||
|
||
### メモリ管理
|
||
|
||
- ホットパスでのアロケーションを最小化
|
||
- 可能な場合はオブジェクトを再利用(`sync.Pool`を検討)
|
||
- 小さな構造体には値レシーバーを使用
|
||
- サイズが分かっている場合はスライスを事前割り当て
|
||
- 不要な文字列変換を避ける
|
||
|
||
### プロファイリング
|
||
|
||
- 組み込みプロファイリングツール(`pprof`)を使用
|
||
- 重要なコードパスをベンチマーク
|
||
- 最適化前にプロファイル
|
||
- まずアルゴリズムの改善に焦点を当てる
|
||
- ベンチマークには`testing.B`の使用を検討
|
||
|
||
## テスト
|
||
|
||
### テスト構成
|
||
|
||
- テストは同じパッケージに保持(ホワイトボックステスト)
|
||
- ブラックボックステストには`_test`パッケージ接尾辞を使用
|
||
- テストファイルは`_test.go`接尾辞で命名
|
||
- テストファイルはテスト対象コードの隣に配置
|
||
|
||
### テスト記述
|
||
|
||
- 複数のテストケースにはテーブル駆動テストを使用
|
||
- `Test_functionName_scenario`を使って説明的にテストを命名
|
||
- より良い構成のため`t.Run`でサブテストを使用
|
||
- 成功ケースとエラーケースの両方をテスト
|
||
- `testify`や類似のライブラリは控えめに使用
|
||
|
||
### テストヘルパー
|
||
|
||
- ヘルパー関数に`t.Helper()`をマーク
|
||
- 複雑なセットアップにはテストフィクスチャを作成
|
||
- テストとベンチマークで使用される関数には`testing.TB`インターフェースを使用
|
||
- `t.Cleanup()`を使ってリソースをクリーンアップ
|
||
|
||
## セキュリティベストプラクティス
|
||
|
||
### 入力検証
|
||
|
||
- すべての外部入力を検証
|
||
- 無効な状態を防ぐため強い型付けを使用
|
||
- SQLクエリで使用する前にデータをサニタイズ
|
||
- ユーザー入力からのファイルパスに注意
|
||
- 異なるコンテキスト(HTML、SQL、シェル)のためデータを検証・エスケープ
|
||
|
||
### 暗号化
|
||
|
||
- 標準ライブラリのcryptoパッケージを使用
|
||
- 独自の暗号化を実装しない
|
||
- 乱数生成にはcrypto/randを使用
|
||
- bcryptまたは類似を使用してパスワードを保存
|
||
- ネットワーク通信にはTLSを使用
|
||
|
||
## 文書化
|
||
|
||
### コード文書化
|
||
|
||
- すべてのエクスポートされたシンボルを文書化
|
||
- シンボル名で文書化を開始
|
||
- 有用な場合は文書化に例を使用
|
||
- 文書をコードの近くに保持
|
||
- コード変更時に文書を更新
|
||
|
||
### READMEと文書化ファイル
|
||
|
||
- 明確なセットアップ指示を含める
|
||
- 依存関係と要件を文書化
|
||
- 使用例を提供
|
||
- 設定オプションを文書化
|
||
- トラブルシューティングセクションを含める
|
||
|
||
## ツールと開発ワークフロー
|
||
|
||
### 必須ツール
|
||
|
||
- `go fmt`: コードフォーマット
|
||
- `go vet`: 疑わしい構造を発見
|
||
- `golint`または`golangci-lint`: 追加リンティング
|
||
- `go test`: テスト実行
|
||
- `go mod`: 依存関係管理
|
||
- `go generate`: コード生成
|
||
|
||
### 開発プラクティス
|
||
|
||
- コミット前にテストを実行
|
||
- フォーマットとリンティングにプリコミットフックを使用
|
||
- コミットを焦点を絞ってアトミックに保つ
|
||
- 意味のあるコミットメッセージを記述
|
||
- コミット前に差分をレビュー
|
||
|
||
## 避けるべき一般的な落とし穴
|
||
|
||
- エラーをチェックしない
|
||
- 競合状態を無視する
|
||
- ゴルーチンリークを作成
|
||
- クリーンアップにdeferを使用しない
|
||
- マップを同時変更
|
||
- nilインターフェースvsポインターを理解していない
|
||
- リソース(ファイル、接続)を閉じるのを忘れる
|
||
- グローバル変数を不必要に使用
|
||
- 空インターフェース(`interface{}`)を過度に使用
|
||
- 型のゼロ値を考慮しない |