--- 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{}`)を過度に使用 - 型のゼロ値を考慮しない