Claude Codeでデバッグするとき、「エラーが出ました、直してください」と丸投げすると、場当たり的な修正が返ってくることがあります。根本原因を特定せずに表面的な修正をすると、別の場所で同じ問題が再発します。
本記事では、/sc:troubleshoot(構造化トラブルシューティング)と/sc:analyze(コード分析)を使ったデバッグの手法と、実際のインシデント対応での活用例を記録します。
「直してください」の問題
よくある失敗パターン
1人間: 「このエラーを直してください」2 TypeError: unsupported operand type(s) for +: 'NoneType' and 'float'3
4AI: 「None チェックを追加しました」5 if value is not None:6 result = value + offset7
8人間: 「テストは通ったけど、なぜ value が None になるのか未解決」Noneチェックの追加はエラーを消しますが、「なぜvalueがNoneなのか」という根本原因は解決していません。上流のデータパイプラインに問題があるかもしれません。
/sc:troubleshoot の構造
/sc:troubleshootは、以下の構造化されたプロセスでデバッグを進めます。
Step 1: 症状の記録
1[症状]2- エラーメッセージ: TypeError: unsupported operand type(s) for +3- 発生場所: unified_analyzer.py:2454- 発生条件: source_alphaの分析時のみ5- 頻度: 毎回6- 環境: 本番(launchd経由)Step 2: 仮説の列挙
AIが複数の仮説を挙げます。
1[仮説]2H1: 外部APIのレスポンスにNullフィールドが含まれている3H2: 中間データのParquetファイルにNaN値がある4H3: 前段の計算で例外がキャッチされ、Noneが返されている5H4: 環境変数の不足で設定ファイルが読めていないStep 3: 仮説の検証(証拠収集)
各仮説を検証するためのコマンドを実行します。
1# H1の検証: APIレスポンスの確認2response = api_client.get_records("source_alpha", count=10)3print(response) # → Nullフィールドなし → H1棄却4
5# H2の検証: Parquetの中身を確認6df = pd.read_parquet("pipeline_source_alpha_*.parquet")7print(df.isnull().sum()) # → moving_avg_14列に3個のNaN → H2有力8
9# H3の検証: 前段の処理のログ確認10# → NaN発生は createIntermediateData.py の出力段階から存在 → H2確定Step 4: 根本原因の特定
1[根本原因]2createIntermediateData.py がAPIから取得したデータの先頭3行では3移動平均(Moving Average)の計算に必要な過去14件のデータポイントが4不足しているため、moving_avg_14がNaNとなる。5
6下流の unified_analyzer.py ではNaN値を想定した処理がないため、7NoneType + float のTypeErrorが発生する。Step 5: 修正と再発防止
1# 根本原因の修正: データ生成時にNaN行を除去2df = df.dropna(subset=["moving_avg_14"])3
4# 防御的チェックの追加(上流が修正されても安全)5if pd.isna(avg_value):6 logger.warning(f"moving_avg_14 is NaN for {source}, skipping")7 continue/sc:analyze の活用
/sc:analyzeは、コード変更の品質・セキュリティ・パフォーマンスを多角的に分析します。
変更前の影響範囲分析
1プロンプト:2/sc:analyze unified_analyzer.py の _analyze_source メソッドに3moving_avg_14 の NaN チェックを追加する変更の影響範囲を分析してください。1分析結果:2[品質]3- 変更箇所の循環的複雑度: +1(許容範囲)4- テストカバレッジへの影響: NaN入力のテストケース追加が必要5
6[セキュリティ]7- 問題なし8
9[パフォーマンス]10- dropna()は全行スキャンのため、大量データでは影響あり11- 推奨: 計算の先頭ウォームアップ期間を除外するアプローチ12
13[影響範囲]14- unified_analyzer.py: 直接変更15- outputFileGenerator.py: moving_avg_14を参照(動作変更なし)1 collapsed line
16- batch_engine.py: 中間データにmoving_avg_14が含まれる前提(変更不要)施策間の競合検出
1プロンプト:2/sc:analyze 施策B3(バッチ分割処理)と施策B4(Z-score閾値判定)の3コードを分析し、リソース操作の競合がないか確認してください。1分析結果:2⚠️ 潜在的リスク検出:3
4B3の _allocate_grid_resource() と B4の _check_zscore_threshold() が5同一データソースのリソースを操作する可能性があります。6
7B3: リソースを確保 → resource_id を取得8B4: 全リソースをスキャン → Z-score 閾値超えで解放9
10B4は「自分が確保したリソース」と「B3が確保したリソース」を11区別できない設計になっています。12
13推奨: リソースにタグ(施策ID)を付与し、14自施策のリソースのみを操作する設計に変更。実際のインシデント対応事例
事例: 7日間の出力ファイル生成停止
症状: 日次の出力ファイル(分析結果を記録したJSONファイル)が7日間生成されていなかった。エラーログなし。
通常のデバッグ: 「ログにエラーがないからわからない」で止まる。
/sc:troubleshootでの対応:
1Step 1 [症状]:2- output_*.json が7日間生成されていない3- launchdジョブは正常実行(launchctl listに表示)4- stderrログにエラーなし5- 最終正常動作: 03-016
7Step 2 [仮説]:8H1: daily_preparation.py が異常終了しているが、exitコード0で終了9H2: データ読み込み対象ファイルが空で、処理がスキップされている10H3: インポートエラーが発生しているが、except句で握りつぶされている11H4: 別施策のデプロイでモジュール名が変更された12
13Step 3 [検証]:14# H4の検証: git log で 03-01 前後のコミットを確認15git log --oneline --since="2026-02-28" --until="2026-03-08"17 collapsed lines
16# → 03-01に施策B4のデプロイあり。core/ 配下のファイル名変更を含む17
18# 当該ファイルのインポートを確認19python -c "from core.shared.tier_classifier import TierClassifier"20# → ModuleNotFoundError! (ただしlaunchd環境ではexceptで握りつぶし)21
22Step 4 [根本原因]:23施策B4のデプロイで _dataProcessingEngineA/core/shared/ 内のモジュール名が24変更されたが、daily_preparation.py では旧名でインポートしていた。25launchd環境では PYTHONPATH の設定により、相対インポート26`from core.shared.X import Y` が解決できず ModuleNotFoundError が発生。27しかし外側の try-except で握りつぶされ、エラーログなしで終了していた。28
29Step 5 [修正]:301. 絶対インポートに変更: from _dataProcessingEngineA.core.shared.X import Y312. CI/CD に絶対インポート強制チェックを追加323. CLAUDE.md にインポートルールを追記この事例が、CLAUDE.mdの「絶対インポートパターン(CI/CD自動検証・BLOCKING)」ルール追加のきっかけとなりました。
/sc:troubleshoot を効果的に使うコツ
1. 症状を正確に記述する
1❌ 「動きません」2✅ 「source_alphaの分析時のみ、unified_analyzer.py:245でTypeError。3 source_betaでは正常。launchd経由の本番環境で発生。」2. ログは省略せずに貼る
1❌ 「エラーが出ました」2✅ 「以下のstderrログを確認してください:3 [2026-03-24 09:05:12] ERROR - TypeError: unsupported operand type(s)4 for +: 'NoneType' and 'float'5 File "/Users/htada/projects/_dataProcessingEngineA/unified_analyzer.py",6 line 245, in _analyze_source」3. 「何を試したか」を伝える
1✅ 「H1(APIレスポンスの問題)は検証済みで棄却。2 H2(Parquetデータの問題)を検証中です。」4. 再発防止策まで求める
1✅ 「修正だけでなく、同じ種類のバグを今後防ぐための2 CLAUDE.mdルールまたはテストを提案してください。」学んだこと
1. 構造化されたデバッグはAIの精度を上げる
仮説→検証→根本原因の流れを明示すると、AIが場当たり的な修正をしなくなります。
2. /sc:analyze は「変更前」に使う
変更後に分析しても手遅れのことがあります。変更前に影響範囲を分析し、問題を予防します。
3. インシデントの教訓をCLAUDE.mdに残す
/sc:troubleshootで特定した根本原因は、CLAUDE.mdのルールとして永続化します。次のセッションでAIが同じ間違いを繰り返しません。
まとめ
構造化デバッグで重要なのは以下の3点です。
- /sc:troubleshoot: 症状→仮説→検証→根本原因→再発防止の5ステップで、場当たり的な修正を防止
- /sc:analyze: 変更前に影響範囲・品質・セキュリティ・パフォーマンスを多角分析
- 教訓の永続化: デバッグで得た知見をCLAUDE.mdのルールに反映し、再発を防止
「エラーを消す」のではなく「エラーの原因を理解し、再発を防ぐ」——構造化されたデバッグがその差を生みます。