golang について
Go 開発向けのスキルで、Google、Uber、Go チームの実践例に基づくガイダンスを Go 1.25 向けにまとめています。一般的な開発テーマを扱い、専門分野別のサブスキルと参考資料も含みます。
主な機能
- 01エラー処理と並行処理の指針
- 02命名、インポート、データ構造の実践
- 03テストとパフォーマンスの推奨事項
- 04Lint、プロジェクト構成、セキュリティ
- 05Go 1.25 の機能と注意点
活用シーン
- 01Go コードの保守性レビュー
- 02Go プロジェクトの規約策定
- 03並行処理とエラー処理の確認
- 04Go のテストと Lint の準備
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ saisudhir14/ claude-skills --skill golangインストール後、Claude Code で/golangを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
README.mdLICENSEreferences/testing.mderror-handling.mdperformance.mdconcurrency.mdpatterns.mdgotchas.md詳細
Go のベストプラクティス
Google、Uber、Go チームの本番環境向けパターンです。Go 1.25 に対応しています。
サブスキル:
skills/go-error-handling、skills/go-concurrency、skills/go-testing、skills/go-performance、skills/go-code-review、skills/go-linting、skills/go-project-layout、skills/go-security。詳細なリファレンスはreferences/にあります。
基本原則
読みやすいコードでは、次の属性をこの順に重視します。
- 明瞭さ:目的と理由が読み手に明確に伝わる
- シンプルさ:最もシンプルな方法で目的を達成する
- 簡潔さ:ノイズに対する情報量が多い
- 保守性:正しく変更しやすい
- 一貫性:周囲のコードベースに沿っている
エラー処理
完全なガイド:
skills/go-error-handling/SKILL.md| リファレンス:references/error-handling.md
- エラーを返し、本番コードでは panic を発生させない
- 呼び出し元で
errors.Is/errors.Asを使う必要がある場合は**%wでラップ**し、境界では%vを使う - コンテキストは簡潔にする:「
failed to create new store: %w」ではなく「new store: %w」 - エラーは一度だけ処理する:同じエラーをログに記録してから返さない
- エラー文字列:小文字で、句読点を付けない
- エラー処理のフローをインデントする:最初にエラーを処理し、正常系のインデントを最小限にする
- 複数の独立したエラーには**
errors.Join**(Go 1.20 以降)を使う - センチネルエラー:変数には
Errの接頭辞、型にはErrorの接尾辞を付ける
if err != nil {
return fmt.Errorf("load config: %w", err)
}
並行処理
完全なガイド:
skills/go-concurrency/SKILL.md| リファレンス:references/concurrency.md
- チャネルのサイズ:0(バッファなし)または1。それ以外は理由を明記する
- ゴルーチンのライフタイムを文書化する:いつ、どのように終了するかを記載する
- エラーを返すゴルーチンには、手動の
sync.WaitGroupより**errgroup.Groupを使う** - 同期関数を優先する:並行処理は呼び出し元に任せる
- ミューテックスのゼロ値:ポインターを使わず、公開構造体に埋め込まない
- 型付きアトミック(Go 1.19 以降):
atomic.Int64、atomic.Bool、atomic.Pointer[T] sync.Map(Go 1.24 以降):互いに異なるキー集合ではパフォーマンスが大幅に向上
g, ctx := errgroup.WithContext(ctx)
g.SetLimit(10)
for _, item := range items {
g.Go(func() error { return process(ctx, item) })
}
return g.Wait()
命名
- 常に MixedCaps を使う:アンダースコアは使わない(
MAX_LENGTHではなくMaxLength) - 頭字語:大文字・小文字の使い方を統一する(
Url、Id、HttpではなくURL、ID、HTTP) - 短い変数名:スコープに応じた長さにする(ループでは
i、グローバル変数ではDefaultTimeout) - レシーバー名:1~2文字の略称を使い、メソッド間で統一する。
this/selfは使わない - パッケージ名:小文字の一語にし、
util/common/miscは使わない - 重複を避ける:
http.HTTPServeではなくhttp.Serve、c.WriteConfigToではなくc.WriteTo
ポインターレシーバーと値レシーバー
| ポインターレシーバー | 値レシーバー |
|---|---|
| レシーバーを変更する | 小さく不変の構造体 |
| 大きな構造体 | 状態を変更しない |
sync.Mutexを含む | マップ、関数、またはチャネル |
| 他のメソッドとの一貫性 | 基本型 |
インポート
import (
"context"
"fmt"
"github.com/google/uuid"
"golang.org/x/sync/errgroup"
"yourcompany/internal/config"
)
- 3つのグループに分ける:標準ライブラリ、外部、内部(空行で区切る)
- 名前の衝突を避ける場合に限り、別名を付ける
- 循環依存のあるテストファイルを除き、ドットインポートは使わない
- ブランクインポート(
import _ "pkg")は、main またはテストでのみ使う
モジュール管理(Go 1.24 以降)
ツールディレクティブを使って、ツールの依存関係をgo.modで管理します。
tool (
golang.org/x/tools/cmd/stringer
github.com/golangci/golangci-lint/cmd/golangci-lint
)
go get -tool golang.org/x/tools/cmd/stringer # add
go tool stringer -type=Status # run
go get tool # update all
go install tool # install to GOBIN
構造体
- 初期化時には常にフィールド名を指定する(位置指定では変更時に問題が起きる)
- ゼロ値のフィールドは省略する
- 公開構造体に型を埋め込まない(意図せず API を公開してしまう)
- ゼロ値の構造体には
varを使う:var user User
スライスとマップ
- nil スライスを優先する:
var t []string(JSON の[]エンコードに限り[]string{}を使う) - 境界でコピーする:変更を防ぐために
copy()またはmaps.Clone()を使う - 事前に割り当てる:サイズが分かっている場合は
make([]T, 0, len(input))を使う slicesとmapsパッケージを使う:slices.Sort、slices.Clone、maps.Clone、maps.Equal
Generics(Go 1.18+)
- 異なる型に対して同一のコードを書く場合に使用します
- 型安全性のために
cmp.Orderedまたはカスタム制約を使用します - ジェネリック型エイリアス(Go 1.24+):
type Set[T comparable] = map[T]struct{} - 過度な汎用化は避けます:具体的な型やインターフェースで十分な場合は、それらを使用します
func Filter[T any](s []T, pred func(T) bool) []T {
result := make([]T, 0, len(s))
for _, v := range s {
if pred(v) {
result = append(result, v)
}
}
return result
}
イテレーター(Go 1.23+)
カスタムイテレーターでは関数を範囲指定して反復処理できます:
func Backward[T any](s []T) func(yield func(int, T) bool) {
return func(yield func(int, T) bool) {
for i := len(s) - 1; i >= 0; i-- {
if !yield(i, s[i]) {
return
}
}
}
}
for i, v := range Backward(items) {
fmt.Println(i, v)
}
文字列/バイト列のイテレーター(Go 1.24+):strings.Lines、strings.SplitSeq、strings.SplitAfterSeq
構造化ログ(Go 1.21+)
slog.Info("user created", "id", userID, "email", email)
slog.With("service", "auth").Info("starting")
handler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo})
slog.DiscardHandler(Go 1.24+)を使うと、テストでログを抑制できます- キー名に一貫性を持たせ、関連するフィールドを
slog.Groupでまとめます
パフォーマンス
詳細ガイド:
skills/go-performance/SKILL.md| リファレンス:references/performance.md
fmtよりstrconv:fmt.Sprintf("%d", n)ではなくstrconv.Itoa(n)を使用します[]byteへの繰り返し変換を避けます:一度保存して再利用します- map の容量を事前確保します:
make(map[string]int, len(items)) - 文字列の連結には
Grow()を使うstrings.Builderを使用します
テスト
詳細ガイド:
skills/go-testing/SKILL.md| リファレンス:references/testing.md
- テーブル駆動テストでは、サブテストに
t.Parallel()を使用します reflect.DeepEqualよりgo-cmpを使用すると、差分を明確に表示できます- 役立つ失敗メッセージ:入力値、実際の値、期待値を含めます
- セットアップの失敗には
t.Fatalを使用します - インターフェースは提供側ではなく利用側に置きます
T.ContextとT.Chdir(Go 1.24+)b.Loop()(Go 1.24+):より簡潔なベンチマークを作成でき、b.ResetTimer()は不要ですsynctest.Test(Go 1.25+):合成時間を使った決定論的な並行テスト
リソース管理(Go 1.24+)
runtime.AddCleanup:オブジェクトごとに複数のクリーンアップを実行でき、循環参照によるリークが発生しません(SetFinalizerの代替)weak.Pointer[T]:キャッシュ、正規化、オブザーバー向けの弱参照os.Root:パス走査攻撃を防ぐ、スコープが限定されたファイルアクセス
パターン
詳細なリファレンス:
references/patterns.md
- 関数型オプション:設定可能なコンストラクターには
WithTimeout(d)、WithLogger(l)を使用します - インターフェースへの適合性:
var _ http.Handler = (*Handler)(nil) - クリーンアップには defer:わずかなオーバーヘッドで安全性を高められます
- グレースフルシャットダウン:シグナル処理と
srv.Shutdown(ctx)を組み合わせます - 列挙値は 1 から開始:0 は無効/未設定を表します
time.Durationを使用します:時間を表す値に生の整数を使用しません- 2 値を返す型アサーション:パニックを防ぐために
t, ok := i.(string)を使用します - コンテキストを最初の引数にします:
func Process(ctx context.Context, ...) - 変更可能なグローバル変数を避けます:依存性注入を使用します
init()を避けます:mainで明示的に初期化します//go:embed(Go 1.16+):静的ファイルを埋め込みます- フィールドタグ:マーシャリングする構造体には
json:"name"を明示します - コンテナーを認識した GOMAXPROCS(Go 1.25+):cgroup に基づくチューニングを自動的に行います
よくある落とし穴
詳細なリファレンス:
references/gotchas.md
| 落とし穴 | 対処法 |
|---|---|
| ループ変数のキャプチャ(1.22 未満) | Go 1.22+ で修正済み(反復ごとの変数) |
| defer は引数を即座に評価する | クロージャー内でキャプチャします |
| nil インターフェースと nil ポインター | nil を明示的に返します |
| エラー確認前に結果を使用する | 必ず先に err を確認します(Go 1.25 で強制) |
| map の反復順序 | slices.Sorted(maps.Keys(m)) でキーをソートします |
| スライスの append で背後の配列が共有される | 完全スライス式 a[:2:2] を使用します |
リント
詳細ガイド:
skills/go-linting/SKILL.md
- 標準のリンター集約ツールとして golangci-lint を使用します
- 推奨リンター:errcheck、govet、staticcheck、revive、gosimple、goimports、errorlint、bodyclose
- ツール依存関係として追加します(Go 1.24+):
go get -tool github.com/golangci/golangci-lint/cmd/golangci-lint - CI で実行します:GitHub Actions では
golangci/golangci-lint-actionを使用します - 抑制は必要最小限にします:
//nolintコメントを使うより、問題を修正します
プロジェクトの構成
完全なガイド:
skills/go-project-layout/SKILL.md
cmd/:実行可能ファイルごとにサブディレクトリを作成し、main.goは薄く保ちますinternal/:Go ツールチェーンによってアクセスが制限されるプライベートパッケージpkg/、src/、models/、utils/は避ける:パッケージ名は目的に沿って付けます- フラットな構成で問題ありません:小規模なプロジェクトでは、深いディレクトリ階層は避けます
- Dockerfile:マルチステージビルドを使い、
CGO_ENABLED=0を設定し、distroless ベースイメージを使用します
セキュリティ
完全なガイド:
skills/go-security/SKILL.md
- パラメーター化 SQL クエリ:ユーザー入力を文字列に埋め込まないでください
os.Root(Go 1.24+):パストラバーサルを防ぐ、スコープが限定されたファイルアクセス- 境界で検証する:型付き構造体にデコードし、フィールドを検証します
- シークレットをハードコードしたり、ログに記録したりしない:
String()で値を秘匿するSecret型を使用します - 標準の暗号機能のみを使用する:ランダムバイトには
crypto/rand、パスワードにはbcryptを使います - HTTP タイムアウト:必ず
ReadTimeout、WriteTimeout、IdleTimeoutを設定します govulncheck:既知の脆弱性がないか、依存関係をスキャンしますgo test -race:CI では必ず競合検出器を有効にして実行します
実験的機能(Go 1.25)
encoding/json/v2:GOEXPERIMENT=jsonv2で有効にします。パフォーマンスが向上し、ストリーミングと呼び出しごとのカスタムマーシャラーに対応しています。
ドキュメント
- コメントは完全な文にし、宣言された名前から始めます
- パッケージコメントは
package宣言の前に置き、間に空行を入れません