体系的にデバッグする
推測とパッチを当てるだけではいけません。各ステップでバグの可能性を絞り込んでいきましょう。
1. 再現する
- 正確なエラー、スタックトレース、ログ、入力、環境(バージョン、OS、設定)を取得します。
- 失敗をオンデマンドで発生させます。理想的には1つのコマンドまたは1つのテストで。断続的に発生する場合は、何が発生確率を高めるか(負荷、タイミング、順序、特定の入力)を見つけます。
- 再現できない場合は、その旨を伝え、コードを変更する前にさらに情報を収集します。
2. 切り分ける
- 再現手順を縮小します。入力を減らし、手順を減らし、コードを減らし、それ以上削るとバグが消えてしまうところまで絞り込みます。
- 以前は動いていたなら、何が壊したのかを見つけます。影響を受けるファイルで
git logを、またはgit bisect run <テストコマンド>を実行します。 - スタックトレースは、フレームワークのコードではなく、あなたのコードの先頭から読みます。
3. 仮説を立てて検証する
- 具体的な仮説を1つ書き留めます。「キャッシュが古いエントリを返すのは、キーがロケールを無視しているからだ」。
- それを証明または反証するチェックを設計します。ログ行、アサーション、デバッガのブレークポイント、単体テストなど。結果を実行前に予測します。
- 結果に驚いた場合、コードに対する自分のモデルが間違っています。次のアイデアを試す前にモデルを更新します。
- 一度に1つのことだけ変更します。役に立たなかった実験は元に戻します。
4. 原因を修正する
- 問題が気づかれた場所ではなく、誤った値が生成された場所を修正します。同じ間違いが他の場所にもないか確認します。
- 修正前は失敗し、修正後は成功する回帰テストを追加します。
- 一時的なログやデバッグコードを削除します。
5. 報告する
根本原因を1~2文で説明し、修正内容、検証方法、および未説明の点があればそれも説明します。