ペアデバッグで分かった、ベテラン技術者がバグ原因を特定するまでの手順

プロマネ#バグ#デバッグ
suin
suin
2015年8月24日 投稿
image.png (38.0 kB)

ソフトウェアには不具合はつきものです。お客さんからバグ報告が上がってくると、厳しい現場ではハラハラするものです。僕も以前、受託開発の現場にいたので、バグ報告を受けるたびにヒヤヒヤしながらバグ原因の特定と修正をしていました。

僕は今でこそ、どんな不具合でも特定できると思えるくらいになりましたが、新米エンジニアだったころは、バグの特定は決して得意といえるものではありませんでした。

当時、僕がデバッグに詰まっていると、先輩や上司のエンジニアは一緒に原因特定を手伝ってくれたり、アドバイスをくれたりしました。その中で僕が学んだことや、当時お世話になったベテランエンジニアのデバッグアプローチを書いてみたいと思います。

# 今日、たまたま丸一日かけるようなデバッグの仕事があり、ちょうどいい機会だと思い記事を書くことにしました。

バグ報告を「勘違い」と決めつけない

とあるシステムで「直近2週間の集計データが正しくない」というバグ報告がお客さんから来たことがあります。どんなシステムかは詳しく言えませんが、過去・未来に関するデータが蓄積されたシステムです。

そのとき、どう調べても過去2週間の集計データは正しく、ロジックにも誤りがないことが確認できました。上司のエンジニアが確認しても同じでした。1日かけても現象が再現せず、くたくただった僕は諦めて「システムは問題ない、何かの勘違いでは?」とお客さんに伝えようかと思いましたが、上司が念のため確認してくれました。

すると、お客さんの言う不具合は「未来2週間の集計データが正しくない」ということでした。つまり、ロジックがおかしいわけでなく、そもそも開発時に要求をミスリードして過去の集計を実装していたわけです。仕様上のミスなわけですが、お客さんにとってはバグです。すぐに仕様の修正にとりかかり、未来の集計にすることで不具合を解決することができました。

中にはお客さんの勘違いで上がってくるバグ報告も無いわけではないですが、短絡的にバグがないと決めつけてはいけないと学びました。もし、あのとき決めつけで「問題はない」と報告していたら、お客さんの信用を失うところでした。

事実を客観的に残す

ベテランエンジニアのデバッグを見ていて2つ目に学んだことは、彼らはバグに関する情報を客観的に残すということです。

バグトラッキングシステムに作られたチケットに対して、判明した事実はコメントでどんどん残していきます。例えば、ログやスクリーンショットです。「こういうログが出たから、○○が原因ではない」とか、スクリーンショットを提示しつつ「この手順で操作すると、画面がこうなる」といった情報を克明に記録します。逆に、推測については「○○があやしいかもしれない」と必ず断定しないコメントになります。

事実を残すことのメリットは、後で誰かが調査を引き継いだときに分かるというだけでなく、思い込みを排除し、頭の中を整理する手助けになります。

再現をするところから着手する

新米エンジニアの僕は、バグ報告が上がってくると、直感で該当コードをいきなりデバッグしたり修正を加えたりしていました。運が良ければ、即座にバグ修正ができるのですが、複数の原因が重なった複雑なバグはそうもいきませんし、よく調べてみたら原因は全く別の箇所だった、ということもありました。

経験のあるエンジニアは、真っ先にバグを再現する手続きを踏んでいました。どのような手順で操作したら、バグが確認するのかをまずハッキリさせます。その上で、その手順を派生させて、バグが再現しないケースを見つけたりして、だいたいの原因に当たりをつけます。その後、「○○が原因ではないだろうか?」といった仮説の検証のフェーズに入っていきます。

再現をさせるプロセスは、一見手間のようですが、結果的に不具合を正しく把握でき、効果的な修正を素早く行うことができるようになります。

再現手順・期待する結果・実際の結果を整理する

上がってくるバグ報告が常にエンジニアが理解しやすい形というわけではありません。まずは、バグ報告をきちんと整理することが大切です。

まずバグの原因を調べる前にやっておくべきことは、次の3つの情報を整理することです。

  1. 再現手順 (Steps)
  2. 期待する結果 (Expected Result)
  3. 実際の結果 (Actual Result)

再現手順は、どのようにしたらバグを再現できるかの情報です。再現する環境や、前提条件、操作方法、入力値などを完結にまとめます。

期待する結果は、再現手順を踏んだ結果、どのような結果になるべきかを説明します。修正は、この期待する結果になるように行います。

実際の結果は、再現手順を踏んだ結果、期待にそぐわなず、どのような結果になってしまうかを記述します。

この3つの情報を整理することで、チームメイトにデバッグを助けてもらうときにバグを説明しやすくなる、自分の思い込みを排除できる等のメリットがあります。お客さんに修正報告をするときの資料に加えることもでき、お客さんがバグが直っているかを確認する手助けにもなります。

仮説を立て、仮説が原因でないことを立証し続ける

不具合の原因を特定するのに当って、原因が複数考えられる場合があります。そういったケースでは、複数の仮説を立てます。

例えば、

  1. 仮説1: DBのプロシージャがバグっているので集計データがおかしい
  2. 仮説2: モデルのロジックがバグっているから集計データがおかしい
  3. 仮説3: Web APIのデータ受け取り処理に問題があって、集計データがおかしくなる
  4. 仮説4: クライアント側のデータ送信処理に問題があって、集計データがおかしくなる

といった具合に複数の仮説を立てます。(立てた仮説はバグチケットに残しておくといいです。)

仮説を立てたら、その仮説ひとつごとにそれが正しくないことを証明します。上の例でいえば、まずはプロシージャが正しく動くことを証明します。SQLを流してみて、その結果問題がないことが分かれば、仮説2の否定に入ります。証明するための手順は、バグチケットに記録していきます。もし、全ての仮説が否定されたら、仮説の中にバグ原因がないことになるので、新たな仮説を考えます。

こうした、仮説を否定しながら証明していく方法「帰無仮説」といいます。帰無仮説のプロセスを繰り返していくことで、バグの原因箇所が徐々に限定されていって、最終的に必ずバグの原因が特定できるわけです。

デバッグスキルは経験によって成長するもの

余談ですが、もし、この記事を読んでいる方が、「バグ原因がすぐに特定できない」「デバッグが苦手」などと苦手意識があったりスキルに不安がある方、あるいは、後輩・部下のデバッグが要領悪いと感じている先輩・上司であれば、ぜひ焦らないでいてほしいです。

デバッグスキルは、どれだけバグに遭遇してきたかといった経験によるところが大きいからです。特に原因箇所の当たりをつける直感力は、経験に大きく左右されます。こうした経験は、場数を踏むほど蓄積されていきます。なので、上で紹介した手順を地道にこなしてほしいです。そうすれば、自ずとスキルが身についてきます。「デバッグスキルは経験によって成長するもの」と気長に構えて、デバッグに当たってみてもらえればと思います。