Agent Grow Advent Calendar 2017 9日目を担当するとっつぁんです。
先日人生の半分くらいプログラムを組んで生活していることに気づき愕然としています。
せっかく長いことプログラムを組んでいたので、私が得意としている(と思い込んでいる)デバッグについて書きたいと思います。
デバッグのコツ
私はいつも以下の手順でデバッグを行っています。
- 再現手順を把握する
- なぜそのような現象が発生するかを想像する
- ちゃんと動いていた時から変更した部分に着目して原因を探す
その結果、8割くらいのバグは発生から30分以内で原因を特定できるようになりました。
※ 慣れてるシステムに関しては バグを見たらバグの原因を特定できる という状態になることもあります
個々の手順について説明する前に、私がデバッグするときの基本方針を説明しておきます。
私がデバッグするときの基本方針
私がデバッグするときに心掛けていることは「バグに関係がありそう処理と確実に関係ない処理を特定していく」ことです。
具体的には、大体関数や機能単位で
- バグと関係ないと考えられる処理
- バグと関係ありそうな処理
- どちらかよくわからない処理
に分類して処理を追っていきます。
最初は「どちらかよくわからない処理」ばかりですが、そこから色々情報を集めてバグと関係ありそうな処理と関係なさそうな処理に分類していきます。
「デバッグ作業」と聞くとふわっとしていてよくわからないかもしれませんが、「バグに関係ありそうな処理と関係なさそうな処理に分類していく作業」と考えるとイメージがわきやすいのではないかと思います。
それではもう少し踏み込んで、どんな手順でデバッグを進めているか説明していきます。
再現手順を把握する
一番大事なことは再現手順を把握することです。
再現手順とは、どういう順番でどんな操作をしたらこのバグが発生したか を示す手順です。
どういう順番でどんな操作したらバグが発生するか という情報は、バグの原因を調査する際にとても大事な情報になります。
なぜなら、バグが発生するときに関連するデータや処理を特定することができるからです。
多くの場合この段階で、大体の処理を「バグと関係ありそうな処理」と「関係なさそうな処理」に分類することができると思います。
場合によっては関係ありそう(なさそう)なデータまで特定できることもあります。
例えば
何回画面を読み込みなおしても表示されている文言が意図していないものになっている
というバグの場合、ネットワークの負荷やサーバの稼働状況などは考慮しなくてもよくなることが多いと思います。
また、
10回だけ表示したいのに11回表示されてしまう
というバグの場合、表示される内容については考慮しなくてもよいと考えられると思います。
こんな感じの視点でどんどん関係ありそうな処理と関係なさそうな処理を分類していきましょう!
・・・たまに再現手順がよくわからないバグというものもありますが、この場合バグに関係あるのかないのかよくわからない箇所がいくつか残ってしまいます。
このようなバグは、バグに関係ありそうな処理と関係なさそうな処理を特定することが困難なため原因を特定するのが難しいことが多いです。。。
なぜそのような現象が発生するのかを想像する
つぎは「どうやったら今実装されている処理でこのバグが発生するか」を想像します。
具体的には ソースコードを見たり該当する処理を思い出したりして、どの変数にどんな値が入っていたらこの現象が起こるか を想像します。
この過程が現場でよく聞く「あたりをつける」という作業なのだと思います。
例えば
何回画面を読み込みなおしても表示されている文言が意図していないものになっている
というバグを例にとります。
この場合、表示項目を設定する処理を見ます。
※ バグが発生したソースの例(PHP)
<div> なんやかんや <div> <?php echo $dispText; ?> <!-- 意図していない文言を表示している箇所 --> </div> </div>
するとバグが発生した箇所では、変数($dispText)をそのまま表示しようとしていることがわかりました。
この場合「変数($dispText)にバグとして表示された予期せぬ文言が設定されていた」ためにこのバグが発生したわけです。
なので変数($dispText)に値を設定している処理に原因があるだろうと考えることができます。
同時に、変数($dispText)に値を設定していない処理はすべてバグに関係ない処理と考えることができます。
ここまでわかったら関係ない処理は無視して、変数($dispText)に値を設定している処理を追っていきましょう。
そしてバグとして表示された文言が設定される箇所を特定しましょう。
ちゃんと動いていた時から変更した部分に着目して原因を探す
私はめんどくさがりなので、おおよそ原因であろう処理を特定してもその全部の処理は追いません。
大体の場合処理を追っていくときには、いままで意図通り動いていたときから変更した処理に絞って処理を追っていきます。
いままで動いていたのに突然バグが発生したということは、最近変更した処理がバグに関係している可能性が高いからです。
私の経験上「変更を行った処理に問題がある」か「変更を行ったときに元々の処理で考慮していないデータパターンが増えてしまった」ことが原因でバグが発生することが多いと感じています。
そのため、変更した処理とその影響を受けてそうな処理を重点的にみていきます。
変更の影響を受けてそうな処理とは「変更箇所で値を変更した変数」や「変更箇所の処理の戻り値」を使っている処理です。
変更箇所に着目してソースを追っていけば、すべての処理を真面目に追っていくよりもはるかに早く原因を特定することができます。
ただし一点だけ注意が必要なことがあります。
元々ちゃんと動いていたことが保証できない場合はこの方法だけではバグの原因を特定できません。
つまり、変更する前から同様のバグが発生する状態になっているような場合は変更箇所だけに着目してもバグの原因は見つかりません。
とはいえ逆に考えれば、変更箇所に問題がなさそうな場合は今までの処理にバグがある と考えることができます。
問題ないと確認した変更箇所はバグに関係ない処理として分類して、別の処理を追っていきましょう
このようにやったことはすべてバグの原因箇所を特定するための材料にして、どんどん問題がありそうな処理を特定していきましょう。
クロージング
私はどんな立派なツールを持っていてもそれを使いこなしていても、バグの原因になりそうな処理を特定できなければ意味がないと考えています。
逆にバグを特定する考え方がわかっていれば、ツールがなくてもある程度バグの原因を特定することができると考えています。
今まで職場でバグの原因を見つける早さに驚かれることが多かったので、ちょっと自慢気な記事を書いてしまいました。
※これは個人の感想です。効果や効能には個人差があります。
という類いの方法だと思いますので、誰にでも効果があるというわけではないと思います。
それでもこの記事が役に立つ人がいれば幸いです。
ここまで読んでいただきありがとうございました。
明日の記事をお楽しみに!