📖 読了目安 約9分
この記事の目次

Step5: デバッグする

動作確認で問題が見つかったら、原因を特定して修正します。デバッグは単なる「バグ探し」ではなく、論理的に原因を絞り込むプロセスです。

なぜデバッグスキルが重要なのか

初心者あるある: 「なんとなくここが怪しい」→ 適当に修正 → 別のバグを生む

デバッグの進め方を間違えると:

  • 時間を浪費: 当てずっぽうで何時間も無駄にする
  • 二次被害: 原因不明のまま修正して、別のバグを生む
  • 再発: 根本原因を理解せず、同じバグを何度も作る

正しいデバッグの考え方:

バグ ≠ 「コードのどこかが間違っている」
バグ = 「期待と実際の差異がある。その原因がどこかにある」

デバッグの基本ステップ

flowchart TD
    A[問題を正確に把握] --> B[仮説を立てる]
    B --> C[仮説を検証]
    C --> D{原因特定?}
    D -->|No| B
    D -->|Yes| E[修正する]
    E --> F[修正を検証]
    F --> G{解決?}
    G -->|No| B
    G -->|Yes| H[完了]

Step1: 問題を正確に把握する

なぜこれが最初か: 問題を正確に理解しないと、見当違いの調査をしてしまう

❌ 曖昧な問題把握
「なんか動かない」
「エラーが出る」
「おかしい」

✅ 正確な問題把握
「注文ボタンをクリックすると、NullPointerExceptionが発生する」
「数量に0を入れると、エラーにならず0円で注文できてしまう」
「商品名が50文字を超えると、画面が真っ白になる」

確認すべきこと:

  • 何が起きているか: エラーメッセージ、画面の状態、ログの内容
  • 何が期待される動作か: 仕様上、どうなるべきだったか
  • 再現手順: どうすればその問題を再現できるか
  • 再現条件: 特定のデータ、環境、タイミングでのみ発生するか

Step2: 仮説を立てる

なぜ仮説が必要か: 「どこが原因かもしれない」を絞り込まないと、全コードを見ることになる

問題: 「注文ボタンクリックでNullPointerException」

仮説例:
1. orderオブジェクトがnullのまま処理している
2. order.getItems()がnullを返している
3. データベースから取得した値がnullになっている

仮説の立て方:

  • エラーメッセージから推測
  • スタックトレースの行番号から推測
  • 最近変更した箇所から推測
  • 似たようなバグの経験から推測

Step3: 仮説を検証する

検証方法を選ぶ:

仮説 検証方法
この変数がnullでは? ログ出力 or デバッガで確認
この条件分岐が間違っている? ブレークポイントで実際の値を確認
このメソッドが呼ばれていない? ログ出力で通過を確認
DBの値がおかしい? SQLで直接データを確認

Step4: 原因を特定する

仮説が正しければ原因特定。間違っていれば新しい仮説を立てる。

検証結果:
✅ 仮説1: orderがnull → 確認したらnullではなかった
✅ 仮説2: order.getItems()がnull → 確認したらnullだった!

原因特定:
→ 新規注文の場合、itemsリストが初期化されていない

Step5: 修正して検証

修正したら、必ず以下を確認:

  • 元の問題が解決したか
  • 他の機能に影響がないか(回帰テスト)

デバッグの具体的なテクニック

テクニック1: ログ出力で追跡

// 処理の流れを追跡
logger.debug("=== 注文処理開始 ===");
logger.debug("入力値: orderId={}, quantity={}", orderId, quantity);

// 中間値を確認
logger.debug("在庫確認結果: hasStock={}", hasStock);
logger.debug("計算結果: total={}", total);

// 分岐の確認
if (hasStock) {
    logger.debug("在庫あり: 注文処理に進む");
} else {
    logger.debug("在庫なし: エラー処理に進む");
}

ログ出力のコツ:

  • 変数の値だけでなく、変数名も出力する
  • 処理の開始・終了が分かるようにする
  • 条件分岐のどちらに入ったか出力する

テクニック2: デバッガを使う

デバッガの活用シーン

1. 複雑な条件分岐
   → ステップ実行でどの分岐に入るか確認

2. ループの挙動
   → 各イテレーションで変数がどう変化するか確認

3. 例外発生箇所
   → 例外ブレークポイントで発生時点の状態を確認

4. 値の変化
   → ウォッチ式で変数の変化を監視

基本操作:

キー 操作 用途
F5 実行継続 次のブレークポイントまで実行
F10 Step Over 次の行へ(関数の中には入らない)
F11 Step Into 関数の中に入る
Shift+F11 Step Out 現在の関数から出る

テクニック3: 二分探索で絞り込む

なぜ二分探索か: 調査範囲を効率的に半分ずつ減らせる

問題: 100行の処理のどこかでデータがおかしくなる

二分探索:
1. 50行目にログを入れる
   → 50行目時点では正常 → 51-100行が原因
2. 75行目にログを入れる
   → 75行目時点で異常 → 51-75行が原因
3. 63行目にログを入れる
   → 63行目時点では正常 → 64-75行が原因
4. ...

6-7回の確認で原因箇所を特定できる

テクニック4: 最小再現コードを作る

複雑な問題の場合: 問題を再現する最小のコードを作る

// 本番コード: 複雑で原因が分からない
public void complexMethod() {
    // 100行のコード...
}

// 最小再現コード: 問題の本質だけ抽出
public void minimalReproduction() {
    List<String> list = null;
    list.add("test");  // ここでNPE!
}

よくあるバグパターンと対処法

パターン1: NullPointerException

// 原因: nullチェックなしでメソッド呼び出し
order.getItems().size();  // orderまたはitemsがnull

// 対処: nullチェックを追加
if (order != null && order.getItems() != null) {
    order.getItems().size();
}

パターン2: 配列の範囲外アクセス

// 原因: インデックスが範囲外
String[] items = new String[3];
items[3] = "test";  // IndexOutOfBoundsException

// 対処: 範囲チェックを追加
if (index >= 0 && index < items.length) {
    items[index] = "test";
}

パターン3: 無限ループ

// 原因: ループ終了条件が満たされない
while (count < 10) {
    // countが増加していない!
    process();
}

// 対処: 終了条件に向かって進んでいるか確認
while (count < 10) {
    process();
    count++;  // 忘れずに増加
}

パターン4: 型変換エラー

// 原因: 文字列を数値に変換できない
int num = Integer.parseInt("abc");  // NumberFormatException

// 対処: 変換前にチェック
if (str.matches("\\d+")) {
    int num = Integer.parseInt(str);
}

パターン5: 条件分岐の漏れ

// 原因: 想定外のケースを考慮していない
switch (status) {
    case "A": handleA(); break;
    case "B": handleB(); break;
    // "C"の場合は何も起きない!
}

// 対処: defaultケースを追加
switch (status) {
    case "A": handleA(); break;
    case "B": handleB(); break;
    default:
        throw new IllegalStateException("Unknown status: " + status);
}

デバッグで陥りがちな罠

罠1: 思い込みで調査する

❌ 「このコードは絶対正しい」と思い込む
→ 結局そこが原因だった

✅ 「どのコードも疑わしい」と考える
→ フラットな目で全体を見る

罠2: 修正を急ぐ

❌ 原因が分からないまま修正
→ 運良く直ったように見えても、根本原因は残っている

✅ 原因を特定してから修正
→ 再発を防げる

罠3: 一度に複数の修正をする

❌ 3箇所同時に修正
→ どの修正が効果があったか分からない

✅ 1箇所ずつ修正して確認
→ 原因と対策の対応が明確になる

罠4: ひとりで抱え込む

❌ 何時間も一人で悩む
→ 時間の無駄、精神的に消耗

✅ 30分悩んだら相談
→ 別の視点で気づくことがある

実践チェックリスト

デバッグ時は、以下を意識しましょう:

問題把握

  • [ ] エラーメッセージを正確に確認した
  • [ ] 再現手順を特定した
  • [ ] 期待動作と実際の動作の差を明確にした

調査

  • [ ] 仮説を立てて検証している
  • [ ] ログやデバッガで事実を確認している
  • [ ] 思い込みで判断していない

修正

  • [ ] 原因を特定してから修正した
  • [ ] 修正後に元の問題が解決したか確認した
  • [ ] 他の機能に影響がないか確認した

次のステップ

バグを修正したら、コードをより良くするリファクタリングを検討します。

Step6: リファクタリングする


関連ページ