概要
自社プロダクトの開発リーダーを努めており、立場上バグ系の問い合わせの一次窓口になっています。そのため、これまで数多くのバグの一次調査や原因特定、場合によっては修正対応も行ってきており、バグの原因の特定の速さにはかなり自信があります。(少なくとも担当プロダクトについては)
そしてどうやら自分の中で無意識に実践しているデバッグのプロセス(原因特定までのプロセス)の型があるなというのに気づきました。
この記事でそのプロセスの型を言語化してみたいと思います。
※ なお本記事でバグと表現しているものは基本的にはWEBシステムのコーディングのバグを指します。
はじめに(一番大事なこと)
これまでのデバッグ業務を通して、バグの原因を特定する上での一番重要だなと思うのが、 「既存の仕様・実装を可能な限り把握しておく」 ことです。とは言え「そんなの当たり前じゃないか、それができれば苦労しないよ」って思われるかと思います。すぐに始められることとしては↓の辺を把握しておくことかなと思います。
- 他のメンバーが今、何の機能を実装しているか、どのファイル、どの関数を改修しているか
- 次のリリースで何の機能、改修がリリースされるのか
バグの問い合わせがあった際に、私はまず最近のリリース内容に原因がなさそうかを疑います。もちろんずっと昔に作り込まれたバグだけど最近まで発覚しなかったというケースもありますが、割合的には直近のリリースが原因になっているパターンの方が多いかなと思っています。そのため直近の改修内容をインプットしておくとバグの原因が見つけやすくなると考えています。
またこれを継続していくことで 「既存の仕様・実装を可能な限り把握しておく」 にも繋がっていきます。さらに既存の実装、仕様の把握不足で、自身が実装する際にバグを作り込んでしまうリスクも抑えることができます。
バグが発生してから原因特定までに考えていること
バグが発生してから原因を特定するまでのプロセスをまとめてみました。もちろんバグの内容によって進め方は異なりますが、一般的なアプリケーションのコードのバグは以下の流れで進めていくことが多いです。
どんなバグであれ、以下の2つがクリアできると原因の特定はもうすぐです。
- バグの再現方法が明確になっている
- バグを作り込んだコミットが特定できている
1. バグの再現方法を明確にしておく
調査を行いやすくするためにバグの再現方法は明確にしておきます。
いわゆる物理エラーであればエラーログ等も残っているため再現させやすいですが、論理エラーの場合は問い合わせ元のユーザーや社員に詳細な条件をヒアリングしながら試行錯誤することもあります。
2. ログを確認する
物理エラーの場合はログを確認して、エラーメッセージを確認します。
この段階で原因の実装に当たりがついている場合は、そこのコードを見にいきます。
Sentryなんかを導入している場合も調査に活用していきましょう。
3. (原因に心当たりがない場合)バグを作り込んだコミットを特定
原因の実装に全く当たりがつかない場合はgit bisect
等を使ってバグを作り込んだコミットを特定します。どのコミットを境にバグが発生するようになったかを特定します。これが特定できれば原因の特定は(きっと)もうすぐです。
ある程度原因のコミットに当たりがつく場合はgit reset --hard
で地道に確認することも多いです。
4. 原因のコードを特定
原因の実装やコミットが特定できたら、根本の原因となっているコードを特定しにいきます。
コードを見てすぐに原因がわかる場合も多いですが、さっぱり・・・という場合は↓の内容を試して当たりをつけることが多いです。
- コードの一部を削除or復活させてみる
コードの一部を削除or復活させてみてバグが再現しなくなれば、そこのコードが原因の可能性が非常に高いと思われます! - 変数の中身の変遷をチェック
変数の中身が意図したものと違うというようなバグの場合、コードの何行目までは意図した値になっていて、何行目から想定外の値になっているのかを確認していきます。フロントでいうとconsole.logとか、Railsでいうとbinding.pryとかを使って変数の中身の変遷をチェックしていきます。
大抵のバグはここまでで原因を特定できることが多いです。
バグが再現しない時に確認すること
特に論理エラーの場合ですが、「バグの再現方法を明確にしておく」が達成できないケースは意外とあります。そんな時に私が確認していることの例をまとめます。
バグ調査全般に言えることですが、先入観を持たずに色々な角度から分析してみることが重要です。
- データをバグ発生環境と揃えてみる
基本の内容かと思いますが、バグに関連するデータをバグ発生環境と全く同じにして再現するか確認します。意外とリレーションを貼ったレコードが関係しているケースもあります。「このマスタデータが原因となっているに違いない」のように決めつけずに、関連する周辺のレコードにまで目を向けて調査すると良いです。 - 類似の画面と比較してみる
例えばAマスタの参照ページで起きるバグの場合、Aマスタの編集ページは問題ないか、Bマスタの参照ページでは問題ないか、のような確認をします。(実装の差分の確認)
以前Aマスタの参照ページ表示時にエラーとなる不具合に遭遇し、どうしても再現ができない状況にありました。調査していく中でAマスタの編集ページではバグが起きないことに気づき、実装の差分を比較したところ詳細ページにはマスタの変更履歴の表示がありました。この変更履歴を管理する実装がバグの原因となっておりました。 - データの変更履歴を追う
データが絡むバグの場合は、その変更履歴を辿ったりもします。想定外の使われ方をしていることに気付けたりもします。(変更履歴を簡単に終えるライブラリを導入しておくと楽です。) - 実行ユーザーの権限をバグ発生環境と揃えてみる
データを揃えるに近いですが、上位の権限だと下位の権限では発生するバグが発生しなかったりする場合もあります。 - 環境起因を疑う
使っているOS,ブラウザ、ネットワーク環境の状態等を確認します。WindowsとMacのOSの違いによるバグや評価対象外のブラウザでのみ発生するバグというのも一定数ありました。
(おまけ)バグの原因特定後に必ず考えていること
バグの原因が特定できた後は修正を進めていくのですが、毎回必ず意識しているのが バグの横展開(他の実装でも同様のバグが存在しないか) です。例えばAマスタで発生したバグであれば、Bマスタでは問題ないか、編集画面で発生したバグであれば参照画面の方は問題ないか、みたいな意識です。
バグの原因を特定できると真っ先に修正を進めたくなりますが、合わせて修正をすべきバグを見落としていないかを常に意識するようにしています。