多くのデバッグに関する本や記事には、デバッガ等のツールを活用した効率的なデバッグ方法について触れられています。デバッグする上でデバッガは絶対に必要なのですが、今の時代SCMほどデバッグのコストとストレスを軽減できるシステムはないはずなのに、あまりそれについて触れられている記事がなかったり、問題が起きた時にSCMを活用できていない方をたまに見ます。
ごく当たり前な話なのですが、特にSCMを使い始めたばかりの学生の方とかに知ってもらいたいので、記事にしました。
調査範囲の絞り込み
新しいバグを作らない唯一確実な方法はソースコードをいじらず、設定ファイルも一切変更しないことです。逆に言うと、新しくバグが生まれた時には直接的であれ間接的であれなにかしらソースコードや設定ファイル等が変更されていると考えるのが妥当です。
「いつ頃は動いていたっけな」「その後どういう変更をしたかな」という曖昧な記憶を元にログを出力してみたり、ブレークポイントを挟んでいけばいつかは原因に辿り着けるかもしれないので、大学受験が終わってやることがなくなった3月の高校三年生や毛染めの間にやる事がなくて仕方が無い場合は、曖昧な記憶だけをもとにデバッグをするのも悪くないかもしれません。
「どういった変更をしたら動かなくなった」を探すのがデバッグの最初のステップですが、幸いgitをはじめとするSCMには「いつどういった変更をしたか」というログがあり、更にはその変更をする前の状態にソースコードを戻す事ができます。
バグは複合的な要因でも起きるので、都合良く一つのコミットに原因が収まっていないことも多いですが、「どの変更を行う前までは動いていたか」「どの変更を行ったら動かなくなったか」がわかれば、バグの原因を調査する範囲を大きく限定できます。あるいは逆にもし昔のバージョンに遡っていっても動かないような場合は、原因がプログラム自身の外側(OSやライブラリ、外部サービス等)にある可能性がわかります。
なお、cloneしたgitレポジトリの場合はgit checkoutを使うことで瞬時にソースコードを特定のコミット時の状態にすることができます。
git checkout コミットのハッシュ
犯人探し
あまり聞こえのよい話ではないですが、バグを見つけたら「犯人」を探さなければ行けません。「犯人」は過去の自分自身であったり、あるいはプロジェクトの他のメンバーだったりします。「犯人」を特定することで他に同じような原因による潜在的なバグが生まれていないか調べたり、同じようなバグの発生を今後繰り返さないように対策をとることが可能になります。
svnやgitといったSCMには「ファイルのどこの行をいつ誰が変更したか」を調べるblameという恐ろしいコマンドがあります。blameコマンドは普通にコマンドラインから使えますが、視認性という点ではgithub等グラフィカルに表示できるものを使うべきです。
githubのblameはかなり便利で、「その行を変更したコミット」の詳細に一発でジャンプすることができ、そこから「同じタイミングで変更されたファイル/内容一覧」を見る事ができます。コミットメッセージも表示されるので、「なんでそういった変更をしたのか」という過去の経緯も辿っていけます。
デバッグ中に怪しいコードをみつけたら、blameコマンドを使って誰がそんなコードを書いたのか調べましょう。そして、この機能により、変なコードを残してしまうとものすごく古いコミットだったとしても一瞬で後から犯人特定されてしまうので、ソースコードをコミットするときは十分な覚悟と注意を持つようにしましょう。
文脈を作る
特に複数人でアクティブに開発をしているような場合、コミットログは色々なメンバーによる色々な作業内容のログで膨大になります。デバッグの過程の中で原因に繋がりそうなコミットを見つけたとしても、それに関連する以前のコミットを膨大な混在したコミットログの中から探していくのは非効率的です。
Issues(BTS)を活用しましょう。githubではコミットメッセージに「refs #1234」等と入力すると、そのコミットがissue #1234と関連づけられ、あとからそのissueに関連するコミットを一覧で見る事ができるようになり、過去の変更を辿っていく効率があがります。(Twitterのハッシュタグのようなもの)
まとめ
以上の三つはどれも当たり前でなんのひねりもない話ですが、デバッグを行う際になにも考えずに手元にあるコードに向き合う前にこれらの選択肢を頭に入れておくと、デバッグの効率/品質をあげていくことができると思います。当然これだけでバグを発見、修正できるわけではないのですが、使わない理由を探すのは誰がいつ書いたかわからない数千行のコードの中からバグの原因を探すのと同じくらい難しいでしょう。