go-modernize について
Go モジュールの Go ディレクティブが許可する機能を使い、コードを段階的に更新するスキルです。言語や標準ライブラリーの移行とバージョンアップ手順を扱います。
主な機能
- 01段階的なモダナイズの作業モード
- 02Go バージョン別の機能リファレンス
- 03公式モダナイザーの利用ガイド
- 04標準ライブラリー API の移行例
- 05バージョン互換性の確認項目
活用シーン
- 01編集中のコードだけをモダナイズ
- 02Go ディレクティブの引き上げを計画
- 03非推奨または旧式の標準ライブラリー利用を更新
- 04パッケージやモジュール全体のモダナイズ
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ eduardo-sl/ go-agent-skills --skill go-modernizeインストール後、Claude Code で/go-modernizeを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/generics.mdgo1.27.mdstdlib-migrations.mdversion-bump.md詳細
Go のモダナイズ
Go は進化します。Go 1.16 向けに書かれたコードと、Go 1.27 を対象にしたコードは同じ見た目であるべきではありません。段階的にモダナイズしてください。まず go.mod を更新し、その後で新しいパターンを採用します。
go.mod の go ディレクティブで指定されたバージョンを超える機能は採用しないでください。ディレクティブは意図的に、独立したコミットで引き上げ、プロジェクトの CI とデプロイ用イメージで実際に実行されるバージョンに限ってください。
詳細なリファレンス資料は、必要に応じて読み込みます。
references/generics.md—interface{}を型パラメーターに置き換える方法、制約、ジェネリックコンテナー、ジェネリクスを使うべきでない場合。references/stdlib-migrations.md— slog、errors.Join、slices/maps ヘルパー、整数範囲ループ、イテレーターの変更前後の例。references/go1.27.md— ジェネリックメソッド、CutLast、URL/ValuesClone、big.Int.Divide、標準ライブラリのuuid、json/v2 のセマンティクス、synctest と httptest の追加機能、ツールの変更。references/version-bump.md—goディレクティブを引き上げる際に発生しうる変更への手順と、リリースごとのチェックリスト(1.25-1.27)。
以下の概要で不十分な場合にのみ、リファレンスファイルを読んでください。
作業モード
- インライン(デフォルト — 開発者が別の作業を進めている場合): すでに変更している行だけをモダナイズします。ほかの改善点は、得られる効果を添えて最後に一度だけ提案してください。関係のないファイルには触れないでください。依頼されていない一括変更は、本来の変更を大量の差分に埋もれさせてしまいます。提案が却下された場合、セッション中に再度持ち出さないでください。開発者がその意向を記録したい場合は、プロジェクトのエージェント設定ファイル(CLAUDE.md、AGENTS.md)に1行追加してください。
- バージョン引き上げ(
goディレクティブを引き上げる場合):ソースを変更しなくても動作が変わります。機能を採用する前に、references/version-bump.mdに従い、前提条件とリリースごとの変更チェックリストを確認してください。 - 一括変更(パッケージまたはモジュールのモダナイズを依頼された場合):以下の手順を実行し、カテゴリごとに1つのコミットを作成します。カテゴリは
go fixの出力、非推奨 API、言語機能、標準ライブラリのヘルパー、テストです。それぞれビルドとテストに成功する状態にしてください。各カテゴリは独立しています。環境が並列サブエージェントに対応している場合は、それぞれにカテゴリを割り当て、別々のブランチまたはワークツリーで作業させてください。
優先順位
時間が限られている場合は、次の順に適用してください。
- 正確性とセキュリティ —
==の代わりにerrors.Is/Asを使う、http.NewRequestWithContext、ユーザー指定のパスにはos.Rootを使う、乱数が何かを保護する箇所ではcrypto/randを使う、非推奨のx/cryptoパッケージを標準ライブラリの同等パッケージに置き換える。 - 可読性 —
any、min/max、整数範囲ループ、slices/maps、cmp.Or、sync.OnceValue、wg.Go、errors.AsType。 - 段階的な移行 — イテレーター、ロガーの
slogへの移行、go.modのツールディレクティブ、標準ライブラリで対応できるようになった依存関係の削除。
モダナイズの手順
-
go.modのgoディレクティブを確認します。使用できる機能は、このディレクティブのバージョンで制限されます。バージョンの引き上げはバージョン引き上げモードの作業であり、機能変更に伴う副次的な変更として行ってはなりません。 -
まず公式のモダナイザーを実行します。機械的な移行を自動で見つけて修正できます。
# Go 1.26+ — the modernizers now live in go fix go fix ./... # Go 1.25 and earlier, or a modernizer newer than your toolchain go run golang.org/x/tools/go/analysis/passes/modernize/cmd/modernize@latest -fix ./...どちらのコマンドもソースを直接書き換えます。実行前にコミットし、差分を確認してください。どちらのコマンドも使用できない場合は、以下の表を手動で適用してください。
-
アナライザーが対象としない、判断が必要な移行(ジェネリクス、イテレーター、ロガーの置き換え)について、以下の表を確認し、個別に適用します。
-
変更のグループごとに
go build ./...とテストスイートを実行します。
Go バージョン別機能一覧
| Go バージョン | 機能 | 対応 |
|---|---|---|
| 1.13+ | errors.Is, errors.As | エラー比較の == を置き換えます |
| 1.13+ | http.NewRequestWithContext | http.NewRequest を置き換えます |
| 1.16+ | embed | go-bindata / packr を置き換えます |
| 1.18+ | ジェネリクス | interface{} を使うユーティリティ関数を置き換えます |
| 1.20+ | errors.Join | 手動でのエラー集約を置き換えます |
| 1.21+ | log/slog | 構造化ログには log の代わりに使用します |
| 1.21+ | slices, maps | 手書きのスライス/マップ用ユーティリティを置き換えます |
| 1.21+ | min, max 組み込み関数 | math.Min/math.Max(float64 専用)を置き換えます |
| 1.21+ | sync.OnceValue, sync.OnceFunc | sync.Once とパッケージ変数の組み合わせを置き換えます |
| 1.22+ | int に対する range | for i := 0; i < n; i++ を置き換えます |
| 1.22+ | ループごとの反復変数 | v := v のシャドーコピーを削除します(go.mod が 1.22+ を示す場合に限ります) |
| 1.22+ | math/rand/v2 | math/rand を置き換え、rand.Seed を削除します |
| 1.22+ | cmp.Or | 「最初のゼロ以外の値」を選ぶ if 連鎖を置き換えます |
| 1.22+ | メソッドとワイルドカードパターンに対応した ServeMux | GET /users/{id} だけに使うルーター依存関係を置き換えます |
| 1.23+ | func に対する range | コールバックベースの反復処理を置き換えます |
| 1.23+ | unique.Make | 手作りの文字列インターン化を置き換えます |
| 1.24+ | for b.Loop() | ベンチマークの for range b.N を置き換えます |
| 1.24+ | t.Context() | テストでの context.Background() を置き換えます |
| 1.24+ | os.Root | 手動のパストラバーサルチェックを置き換えます |
| 1.24+ | go.mod のツールディレクティブ | tools.go のブランクインポートファイルを置き換えます |
| 1.24+ | runtime.AddCleanup | runtime.SetFinalizer を置き換えます |
| 1.24+ | omitzero 構造体タグ | Go のゼロ値(例:time.Time{})を省略する必要がある場合に、omitempty を置き換えます |
| 1.24+ | strings.SplitSeq, strings.Lines, FieldsSeq | スライスを range で反復するだけの場合に、Split を置き換えます |
| 1.24+ | crypto/sha3, crypto/hkdf, crypto/pbkdf2 | golang.org/x/crypto の各機能を置き換えます |
| 1.24+ | crypto/rand.Text | 手作りのランダムトークンエンコーダーを置き換えます |
| 1.25+ | testing/synctest | 並行処理のテストでの time.Sleep を置き換えます |
| 1.25+ | sync.WaitGroup.Go | wg.Add(1) と go func(){defer wg.Done()} の組み合わせを置き換えます |
| 1.25+ | reflect.TypeAssert | v.Interface().(T) を置き換えます(割り当てなし) |
| 1.25+ | コンテナーを考慮する GOMAXPROCS | go.uber.org/automaxprocs を削除します |
| 1.26+ | errors.AsType | 宣言済みのターゲット変数を使う errors.As を置き換えます |
| 1.26+ | slog.NewMultiHandler | 手書きのファンアウトハンドラーを置き換えます |
| 1.26+ | new(expr) | アドレスを取得するための一時変数を置き換えます |
| 1.26+ | httputil.ReverseProxy.Rewrite | 非推奨で安全性に問題のある Director フックを置き換えます |
| 1.27+ | ジェネリックメソッド | 1 つの型に属するパッケージレベルのジェネリックヘルパーを置き換えます(インターフェースで公開する必要がある場合を除く) |
| 1.27+ | リテラルのキーとしての昇格フィールド | Outer{Inner: Inner{ID: x}} のネストを置き換えます |
| 1.27+ | strings.CutLast, bytes.CutLast | LastIndex とスライス操作を置き換えます |
| 1.27+ | url.URL.Clone, url.Values.Clone | 手書きのディープコピーを置き換えます |
| 1.27+ | big.Int.Divide | QuoRem と符号補正を置き換えます |
| 1.27+ | 標準ライブラリの uuid | v4/v7 ID に google/uuid を使う代わりに採用します |
| 1.27+ | encoding/json/v2 | 呼び出し箇所ごとに採用します — セマンティクスがより厳格です。リファレンスを参照してください |
| 1.27+ | synctest.Sleep, httptest.NewTestServer | time.Sleep+Wait とソケットに依存するテストサーバーを置き換えます |
| 1.27+ | testing/cryptotest.SetGlobalRandom | テストでの tls.Config.Rand を置き換えます |
移行例
上記の各行の変更前/変更後の例は、
references/stdlib-migrations.md(標準ライブラリと言語機能)および
references/generics.md(型パラメーター)にあります。テスト固有の移行 —
t.Context()、b.Loop()、synctest — は go-test-quality
スキルが担当します。
確認チェックリスト
go.modのバージョンが、コードベースで使用している機能に合っているanyや型パラメーターを使うほうが明確な箇所で、interface{}を使っていない- 構造化ログには
log.Printfではなくlog/slogを使っている - エラー文字列を手動で連結するのではなく、
errors.Joinを使っている - 手書きのループを
slices.Contains、slices.SortFunc、maps.Cloneに置き換えている - 該当する箇所では、整数の範囲指定(
for i := range n)を使っている http.NewRequestではなくhttp.NewRequestWithContextを使っているsort.Sliceを使わず、cmp.Compareとslices.SortFuncを使っている- 型安全なコンテナーやユーティリティにはジェネリクスを使い、単純なケースでは使いすぎていない
- サードパーティー依存関係を、最近の Go バージョンで追加された標準ライブラリの代替手段と比較して評価している
- 手動の
Add/Doneペアではなく、sync.WaitGroup.Goを使っている(Go 1.25 以降) - テストで
t.Context()とfor b.Loop()を使っている(Go 1.24 以降) tools.goをgo.modの tool ディレクティブに置き換えている(Go 1.24 以降)