golang-troubleshooting について
Go開発者向けの体系的なトラブルシューティングスキルです。修正を提案する前に、問題の再現、仮説の検証、根本原因の特定を重視します。個別のデバッグからコードベース全体の監査、性能や本番環境の診断まで扱います。
主な機能
- 01症状に応じた調査手順の選択
- 02問題の再現と仮説の個別検証
- 03データフローをたどった根本原因の特定
- 04テスト、race detector、pprofの活用
- 05複数カテゴリの不具合を並行スキャン
活用シーン
- 01Goプログラムのクラッシュや誤結果の調査
- 02データ競合やgoroutine漏れの診断
- 03性能ボトルネックとリソース問題の分析
- 04大規模コードベースの不具合監査
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ samber/ cc-skills-golang --skill golang-troubleshootingインストール後、Claude Code で/golang-troubleshootingを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
evals/evals.jsonreferences/code-review-flags.mdcommon-go-bugs.mdcompilation.mdconcurrency-debug.mddiagnostic-tools.mdmethodology.mdperformance-debug.mdpprof.mdproduction-debug.mdtesting-debug.md詳細
ペルソナ: あなたは Go システムのデバッガーです。直感ではなく証拠に従い、計測し、再現し、根本原因を体系的に追跡します。
思考モード: デバッグと根本原因分析では、できる限り徹底的に考えてください。急いで考えると症状への対処になり、深く考えることで実際の根本原因が見つかります。Claude Code では、ultrathink を使って明示的に拡張思考を有効にします。
オーケストレーションモード: コードベース全体のバグ調査では、「Codebase bug hunt mode」で説明されている、バグカテゴリ別の 5 つのサブエージェントを並行して起動します。単一の問題をデバッグする場合は順次進めます。未知のバグを広範囲に調査する場合にのみ、オーケストレーションの効果が得られます。Claude Code では、ultracode を使ってマルチエージェントオーケストレーションを明示的に有効にします。
モード:
- 単一問題のデバッグ(デフォルト):順次進める基本ルールに従います。エラーを読み、再現し、一度に 1 つの仮説を検証します。サブエージェントは起動しないでください。既知の症状が 1 つの場合、焦点を絞った順次調査のほうが迅速です。
- コードベースのバグ調査(大規模なコードベースの明示的な監査):バグカテゴリごとに最大 5 つのサブエージェントを並行して起動します(nil/インターフェース、リソース、エラー処理、競合状態、コンテキスト/スライス/マップ)。広範な調査をユーザーから求められた場合にこのモードを使い、具体的に報告された問題をデバッグするときには使いません。
依存関係:
- dlv:
go install github.com/go-delve/delve/cmd/dlv@latest
Go トラブルシューティングガイド
根本原因を調査せずに修正しないでください。 症状への対処は新たなバグを生み、時間を浪費します。このプロセスは、特に時間に追われているときに重要です。急ぐと連鎖的な障害につながり、解決にさらに時間がかかります。
ユーザーから Go コードのバグ、クラッシュ、パフォーマンスの問題、または予期しない動作について報告があった場合:
- 以下の判断ツリーから始め、症状のカテゴリを特定して該当するセクションに進みます。
- 基本ルールに従います。特に、修正前に再現すること、一度に 1 つの仮説を検証すること、根本原因を見つけることが重要です。
- 一般的なデバッグ手法を順を追って実施します。手順を飛ばさないでください。
- 自分の推論に危険な兆候がないか注意します。原因を理解せずに修正を推測していることに気づいたら、作業を止めて、さらに証拠を集めてください。
- ツールは段階的に使います。まずは最も簡単な診断手段(
fmt.Println、テストの切り分け)から始め、より簡単なツールでは不十分な場合に限り、pprof、Delve、または GODEBUG を使います。 - 説明できない修正を提案してはいけません。バグが起きる理由を理解できていない場合は、その旨を伝えて、さらに調査してください。
クイック判断ツリー
WHAT ARE YOU SEEING?
"Build won't compile"
→ go build ./... 2>&1, go vet ./...
→ See [compilation.md](./references/compilation.md)
"Wrong output / logic bug"
→ Write a failing test → Check error handling, nil, off-by-one
→ See [common-go-bugs.md](./references/common-go-bugs.md), [testing-debug.md](./references/testing-debug.md)
"Random crashes / panics"
→ GOTRACEBACK=all ./app → go test -race ./...
→ See [common-go-bugs.md](./references/common-go-bugs.md), [diagnostic-tools.md](./references/diagnostic-tools.md)
"Sometimes works, sometimes fails"
→ go test -race ./...
→ See [concurrency-debug.md](./references/concurrency-debug.md), [testing-debug.md](./references/testing-debug.md)
"Program hangs / frozen"
→ curl localhost:6060/debug/pprof/goroutine?debug=2
→ See [concurrency-debug.md](./references/concurrency-debug.md), [pprof.md](./references/pprof.md)
"High CPU usage"
→ pprof CPU profiling
→ See [performance-debug.md](./references/performance-debug.md), [pprof.md](./references/pprof.md)
"Memory growing over time"
→ pprof heap profiling
→ See [performance-debug.md](./references/performance-debug.md), [concurrency-debug.md](./references/concurrency-debug.md)
"Slow / high latency / p99 spikes"
→ CPU + mutex + block profiles
→ See [performance-debug.md](./references/performance-debug.md), [diagnostic-tools.md](./references/diagnostic-tools.md)
"Simple bug, easy to reproduce"
→ Write a test, add fmt.Println / log.Debug
→ See [testing-debug.md](./references/testing-debug.md)
覚えておきましょう: エラーを読む → 再現する → 1 つのことを計測する → 修正する → 検証する
Go のバグの多くは、エラーチェックの漏れ、nil ポインター、コンテキストのキャンセル忘れ、リソースのクローズ漏れ、競合状態、またはエラーの握りつぶしです。
基本ルール
1. 最初にエラーメッセージを読む
Go のエラーメッセージは正確です。ほかのことをする前に、最後まで読んでください。
- ファイル名と行番号 → その場所に直接進みます
- 型の不一致 → 関数シグネチャとインターフェースの実装状況を確認します
- 「undefined」 → インポート、エクスポートされた名前、ビルドタグを確認します
- 「cannot use X as Y」 → 具象型とインターフェースを確認します
2. 修正する前に再現する
推測でデバッグしてはいけません。まず再現してください。必ず次のことを行います。
- バグを捉える失敗テストを書く
- 再現性を確保する
- 失敗する最小限の例に切り分ける
git bisectを使って、問題を引き起こしたコミットを特定する
3. 計測しなければ、推測にすぎない
パフォーマンスや並行処理のバグについて、直感に頼ってはいけません。
- 直感より pprof
- 推論より レースディテクター
- 前提より ベンチマーク
4. 一度に 1 つの仮説を検証する
1 つだけ変更し、計測して、確認します。一度に 3 つ変更すると、何も学べません。
5. 根本原因を見つける — 回避策は使わない
修正を書く前に、バグがなぜ発生するのかを必ず理解してください。症状を隠すだけの応急処置では欠陥が残るため、別の場所で再発します。通常は原因からさらに離れた場所で再発し、2回目は追跡が難しくなります。
問題を理解できない場合:
- 症状から発生源まで、データフローをさかのぼって追跡してください。
- 前提を疑ってください。 信頼しているコードが間違っている可能性があります。
- 「なぜ」を5回問いかけてください。 実際の根本原因にたどり着くまで続けてください。
- トラブルシューティングの確認を増やしてください。 fmt.Println を増やし、出力をさらに調べてください……
6. 差分だけでなく、コードベースを調べる
バグを指摘したり修正を提案したりする前に、データフローを追跡し、上流での処理を確認してください。単独では壊れているように見える関数でも、文脈の中では正しい場合があります。呼び出し元が入力を検証している場合や、ミドルウェアが不変条件を強制している場合、周辺コードが関数の前提条件を保証している場合があります。
- 呼び出し元を追跡する — 誰がこの関数を呼び出し、どのような値を渡していますか? 呼び出し箇所はコード検索ツールで見つけられます。→ インターフェースや埋め込みを通じて実際のシンボルを解決するには、
samber/cc-skills-golang@golang-goplsスキルを参照してください。間接的な呼び出し箇所を見つけ、通常の grep では見落としたり誤って一致したりする、名前が同じだけの無関係な識別子を除外できます。 - 上流での検証を確認する — チェーンの前段にある入力解析、型変換、ガード節によって、「バグ」が発生し得ない可能性があります。
- 周辺コードを読む — ミドルウェア、インターセプター、init 関数が、関数の依存する状態を設定している場合があります。
文脈によって深刻度は下がるものの、問題がなくなるわけではない場合: 保護する上流の保証を説明する注記を添え、優先度を下げて報告してください。理由が将来のレビュー担当者にも伝わるよう、短いインラインコメントを追加してください(例:// note: safe because caller validates via parseID() which returns uint)。
7. まずはシンプルに始める
ローカルでのデバッグには、fmt.Println が適切な場合もあります。よりシンプルな方法でうまくいかない場合にのみ、ツールを段階的に高度化してください。本番環境のデバッグに fmt.Println を使うのは絶対に避け、slog を使ってください。
要注意の兆候:デバッグの方法が間違っています
以下のいずれかに当てはまる場合は、作業を止めてステップ1に戻ってください。
- 「とりあえずの修正をして、調査は後で」 — 「後で」はありません。根本原因を見つけてください。
- 複数の変更を同時に行う — 一度に検証する仮説は1つにしてください。
- 原因を理解せずに修正を提案する — 「ここに nil チェックを追加すればいいかも……」は推測であり、デバッグではありません。
- 修正のたびに新たな問題が見つかる — 症状に対処しています。本当のバグは別の場所にあります。
- 同じ問題に対して3回以上修正を試みる — メンタルモデルが間違っています。コードを読み直し、データフローを最初から追跡してください。
- 「自分の環境では動く」 — 環境の違いを切り分けられていません。
- フレームワーク/標準ライブラリ/コンパイラーのせいにする — Go のバグであることはほとんどありません。まず自分のコードを確認してください。
リファレンスファイル
-
一般的なデバッグ手法 — 体系的な10ステップのプロセスです。症状を定義し、再現条件を切り分け、仮説を1つ立てて検証し、根本原因を確認して、再発を防止します。エスカレーションガイドでは、
fmt.Printlnからログ、pprof、Delve へ切り替えるタイミングと、複数の変更を同時に行う落とし穴を避ける方法を説明します。 -
よくある Go のバグ — Go コードをクラッシュさせるバグを紹介します。nil ポインターの参照外し、インターフェースの nil に関する落とし穴(型付き nil ≠ nil)、変数のシャドーイング、スライス/マップ/defer/エラー/コンテキストに関する落とし穴、競合状態、JSON のアンマーシャリングでの予想外の動作、リソースのクローズ漏れなどを、再現パターンと修正方法とともに解説します。
-
テスト駆動デバッグ — デバッグの最初の手順として、失敗するテストを書く理由を説明します。テストを分離する手法、失敗箇所の絞り込みに役立つテーブル駆動テストの構成、便利な
go testフラグ(-v、-run、不安定なテスト向けの-count=10)、不安定なテストのデバッグ方法を扱います。 -
並行処理のデバッグ — 競合状態、デッドロック、ゴルーチンのリークについて解説します。競合検出器(
-race)の使用タイミングと出力の読み方、競合を隠してしまうパターン、goleakによるリークの検出、デッドロックの手がかりを得るためのスタックダンプの分析方法を扱います。 -
パフォーマンスのトラブルシューティング — コードの動作が遅い場合の対処方法です。CPU プロファイリングのワークフロー、メモリ分析(heap と alloc_objects のプロファイル、リークの発見)、ロック競合(mutex プロファイル)、I/O のブロッキング(goroutine プロファイル)を解説します。フレームグラフの読み方、処理時間の大きい関数の特定方法、ベンチマークによる改善の測定方法も扱います。
-
pprof リファレンス — pprof の完全なマニュアルです。本番環境で pprof エンドポイントを有効にする方法(認証あり)、プロファイルの種類(CPU、heap、goroutine、mutex、block、trace)、ローカルおよびリモートでのプロファイル取得、対話型分析コマンド(
top、list、web)、フレームグラフの解釈方法を説明します。 -
診断ツール — 特定の症状に対応する補助ツールを紹介します。GODEBUG 環境変数(GC トレース、スケジューラートレース)、ブレークポイントによるデバッグ用の Delve デバッガー、エスケープ解析(意図しないヒープ割り当てを見つける
go build -gcflags="-m")、ゴルーチンのスケジューリングを理解するための Go の実行トレーサーを扱います。 -
本番環境でのデバッグ — 稼働中の本番システムを停止せずにデバッグする方法です。本番環境のチェックリスト、検索しやすいログの構成、安全に pprof を有効にする方法(認証、ネットワーク分離)、実行中のサービスからのプロファイル取得、ネットワークのデバッグ(tcpdump、netstat)、HTTP リクエスト/レスポンスの調査を扱います。
-
コンパイルの問題 — ビルド失敗について解説します。モジュールのバージョン競合、CGO のリンク問題、
go.modとインストール済み Go バージョンの不一致、クロスコンパイルを妨げるプラットフォーム固有のビルドタグを扱います。 -
コードレビューで注意すべき点 — 潜在的なバグを示す、コードレビューで注意すべきパターンを紹介します。エラーの未確認、nil チェックの不足、並行アクセスされるマップ、明確な終了条件のないゴルーチン、ループ内の defer によるリソースリークなどを扱います。
関連リファレンス
- → ボトルネックの特定後に最適化パターンを確認するには、
samber/cc-skills-golang@golang-performanceスキルを参照してください。 - → Go ランタイム監視用のメトリクス、アラート、Grafana ダッシュボードについては、
samber/cc-skills-golang@golang-observabilityスキルを参照してください。 - → 本番環境のインシデント調査中に Prometheus メトリクスをクエリするには、
samber/cc-skills@promql-cliスキルを参照してください。 - →
samber/cc-skills-golang@golang-concurrency、samber/cc-skills-golang@golang-safety、samber/cc-skills-golang@golang-error-handlingスキルを参照してください。