デバッグ。プログラミングに内在するバグ(不具合)を除くための作業です。
プログラムには、バグは必ず存在します。したがってデバッグ作業も、プログラマーには必須の作業となります。
このデバッグ作業、実は、人によって効率の差が顕著に表れます。
- 初心者プログラマーが、3日かかったデバッグ作業を
- ベテランプログラマーなら、3分で解決する
こんなことも、ザラに起こります。
何が違うのでしょう?デバッグには、意識すべき基本の心得があるのです。
こんなデバッグはいけない
プログラミング初心者は、バグに遭遇すると、軽くパニックになりますよね。
あれ?なんか謎のエラーメッセージで止まる・・・
ここに出るはずの結果が表示されない・・・
そして、おもむろにコードをいじり始めます。
ここを直せばいいのかな?
カチャカチャ・・・
でもなかなか直りません。それどころか、
あれ?さらにおかしくなった・・・
そして、泥沼にはまっていくのです。
待って待って!
デバッグは、やみくもにやっちゃダメ!
デバッグには、それなりのお作法や心得が、あるのです。
デバッグにおける、基本の十ヶ条
デバッグには、心得ておくべき基本事項が、いくつもあります。最も基本は、次にあげる十ヶ条。
デバッグを始める前の心得
- 再現方法を確かめておく
- ログやトレースを確認しておく
- 仮説を立てる
デバッグ中の心得
- 推測ではなく事実を確認する
- 二分探索で原因箇所を絞り込む
デバッグが終わった後の心得
- 同様の修正を水平展開する
- リグレッションテストを行う
今後デバッグしやすくするための心得
- コンパイルやリントによるエラー検出を考える
- 契約による設計を考える
- リファクタリングを考える
デバッグは、段取りよくやるのが大事!
順番に見ていきましょう。
デバッグを始めるまえの心得
プログラムにバグが発生!さっそくデバッグ開始・・・の前に、ちょっと待って。
バグが発生したからといって、すぐにコードをさわり始めるのはNGです。
でも、コード直しながら確認したほうが、早いのでは?
下準備をちゃんとすることが、結局は近道だよ!
なにごとも、準備が大切。それにより、後の混乱が防げます。
(1)再現方法を確かめておく
デバッグした後は、ほんとうにバグが直っているのか、確かめる必要があります。
確かめ方はこうですね。直す前とまったく同じことをして、ちゃんと動作するか。
つまり、どういう手順でやったらバグが起こるのか、事前に確認しておくことが大事です。
特に、このようなときは要注意。
- 環境により動きが変わるもの 自分のPCで動くけど、あなたのPCで動かない
- 日付や時刻で動きが変わるもの 特定の日や特定の時刻でだけ起こるもの
- 起きたり起きなかったりするもの マシンのメモリや負荷状況によるもの、並列処理によるもの
必ず、バグが再現できる手順を確認しておきましょう。
(2)ログやトレースを確認しておく
デバッグとは、バグがありそうな箇所を推理する作業、といえるでしょう。
推理に重要なのは、証拠です。
バグの状況が再現できたら、あらかじめ証拠を集めておきます。
- 表示内容
- 送信内容
- 保存結果
さらには
- プログラムが出力するログ
- エラー時に表示されるトレース情報(プログラムの実行履歴)
いろいろなものを証拠として集めておきましょう。
特に、再現しにくいバグの場合、ログやトレースで内部状態を追跡することも重要になってきます。
忘れずに収集しておきましょう。
(3)仮説を立てる
デバッグを始めるための、証拠が揃いました。
次に重要なことは、証拠をもとに仮説を立てること。
- 例えば、エラーメッセージが発生していたら?おそらく、コードでメッセージを出力している箇所から、バグが追跡できるでしょう。
- 期待と異なるような結果が出るようなケースなら?その結果を作り出しているデータか、変換過程に、バグが潜んでいそうです。
仮説を立てることにより、調査箇所をあらかじめ絞り込みます。そうしないと、後の作業がとりとめもないものになってしまいます。
デバッグ中の心得
それでは、いよいよデバッグの開始です。
仮説に基づき、なぜバグが起こるのかを明らかにしつつ、コードを直していきます。
といっても、
コードのどこを直せばいいのか、迷いますよね・・・
大事なのは、「事実を確認しながら」「少しずつ進める」こと!
やみくもにコードに手を出してはいけません。状況を見ながら、少しずつ進めます。
(4)推測ではなく事実を確認する
デバッグのとき、一番厄介となる敵。それは一体何でしょう?
それは、「思い込み」です。
- ここはこう動いているだろう
- ここはこういう結果が出力されているだろう
一度そう思うと、なかなかそこから抜け出せず、泥沼にはまります。
先入観に囚われず、事実を確認しましょう。
- 変数やデータがどうなっているか
- コードのどこを通っているのか
変数の中身や実行状況を確認する用に、大抵のプログラミング言語は、デバッガを提供しています。
デバッガを使うのが難しいなら、ひたすらprint文で変数を表示しまくるのもアリですよ。(俗に言う「プリントデバッグ」)
(5)二分探索で原因箇所を絞り込む
デバッグで重要なことは、バグが起こっている箇所を特定すること。
そのときに大事な考え方が、二分探索です。
検査すべきコードの中間あたりで、事実(変数の中身など)を確認する
- その場所での結果がすべて期待どおりなら、バグは、その場所の「後」にある
- その場所で既に期待と違う結果であるなら、バグは、その場所の「前」にある
これで、バグが存在しそうな場所が、前半か後半か、半分に絞られるわけです。
範囲が絞られたら、さらにその範囲で、二分探索。
こうやって、見るべき場所を絞り込んでいき、バグの箇所を特定していくわけです。
もしそれでもバグの箇所が見つからなかったら?それはおそらく、仮説が間違っていたのでしょう。あらためて仮説に立ち戻りましょう。
デバッグとはつまり、
- 証拠を集め
- 仮説を立て
- 事実を確認していく
このサイクルの繰り返しなのです。
デバッグが終わったあとの心得
バグの箇所が特定でき、コードの修正が完了。期待通りに動くようになりました。
やったー!バグが直りましたよ!
おめでとう!でもまだまだ、やることあるよ!
そう、バグが直ったからといっても、まだまだやるべきことがあるのです。
(6)同様の修正を水平展開する
バグの原因は、割と単純なことだったりもしますね。
- ファイルの操作誤り
- ライブラリ操作の誤認識
こういう単純なバグは、もしかしたら、他のところでも同じことをやっているかも?
同じようなバグが他の箇所にもあれば、同じように直しましょう。
こういった作業を、「横展開」とか「水平展開」と呼びます。水平展開すべき箇所はないか、確認しましょう。
(7)リグレッションテストを行う
デバッグでコードを修正したときに、注意しなければいけないのが、デグレードです。
- ここのバグは確かに直ったけど
- そのせいで、今まで動いていたとこが動かなくなっちゃった!
バグを修正したら、そのバグの箇所はもちろん、その周辺のコードもテストします。デグレードが発生していないかを確認するのです。
デグレードがないことを確認するテストのことを、リグレッションテストといいます。
リグレッションテストを忘れずに行いましょう。
リグレッションテストに有効なのが、ユニットテストの仕掛けです。テストを自動化しておくと、リグレッションテストが格段にラクになります。可能であれば導入を検討してみましょう。
今後デバッグしやすくするための心得
横展開で修正し、リグレッションテストもパス。ひととおりデバッグの作業は終了です。
いやーデバッグって大変ですね・・・
でも、まだ考えることがあります。それは今後のこと。
プログラムにバグがある以上、デバッグという作業はなくなりません。
でも、デバッグをラクにするために、事前にできること、はあるのです。
そもそも、デバッグしやすいコードを書くことが大切だよ!
(8)コンパイルやリントによるエラー検出を考える
デバッグをラクにする方法のひとつは、バグを早く見つけること。
できれば、プログラムを動かす前に見つけらるなら、それにこしたことはありません。
今のプログラミング言語では、動作する前に問題点を見つけてくれる仕掛けが、いろいろあります。
- コンパイル型言語なら、コンパイル時の警告で、問題の起こりそうな箇所を教えてくれます
- リンターと呼ばれる、コードをチェックするツールも、積極的に使いましょう。
コンパイル時点で、バグの芽を早めに摘んでおく。これが深刻な不具合の防止に役立ちます。
(9)契約による設計を考える
あるところで発生した小さなバグが、いろいろな箇所に派生し、深刻な不具合になる場合もあります。
バグは、できるだけ早い段階で検出できるほうがよいのです。
その検出方法のひとつが「契約による設計」です。
- プログラムが、動作中に守るべき事項(=契約)を定める
- その契約内容をコードに埋め込む
- 契約が破られた場合、プログラムを停止する
不適切なデータなど、契約違反が起これば、プログラムは直ちに停止。そこから先に不具合が波及することを防止できるのです。
(10)リファクタリングを考える
バグが発生しやすいコード、デバッグがしにくいコード。
それは経験則として、こんなコードでしょう。
- 分かりにくいコード
- 複雑なコード
プログラムは、書き方によって簡単になったり複雑になったりします。
なので、同じことをするなら、簡単で単純なコードであるほうが、デバッグがしやすくなります。
読みやすいコードを書きましょう。読みやすければ、間違いも発見しやすくなります。
さらには、ちゃんと内部構造を改善し、分かりやすいコードに改善できるとベストです。
そういう修正のことを、リファクタリングといいます。
可能であれば、リファクタリングにも挑戦する価値があります。今後のデバッグ作業を格段にラクにしてくれるはずです。
デバッグにAIを活用しよう
デバッグ作業は、ある意味、職人技とも言えるもの。
ですが近年では、AIの登場により、デバッグ作業は格段にラクになってきています。
例えば、このようなことができます。
バグが発生したとき
- 対象のソースコード
- 発生したエラーメッセージ
- 期待する結果
- 実際に起こる結果
といった情報をChatGPTなどのAIに入力
「バグの原因を教えて」と聞く
これでけっこう、コードの内容やバグの原因を教えてくれたりします。
デバッグにAIを使うときの、コツって何かあります?
「事実をありのままに伝える」のがポイントだね!
AIに聞くとき、良かれと思い「ここがおかしいと思うんだけど」みたいな情報を加えてしまいがち。
しかしそれが、思い込みの解釈であった場合、AIをミスリードしてしまいます。
対象のコード、発生したメッセージなど、事実をありのまま伝えましょう。そのほうが正確な回答を得やすくなります。
デバッグにおけるAIの活用は、まだまだ発展途上です。今後さまざまな研究がなされ、進化していくでしょう。
活用しない手はないですね。
まとめ
実はプログラマーは、コーディングする時間と同じくらい(いやそれ以上に)デバッグに時間を費やします。
そしてデバッグ作業は、技量により、効率に圧倒的な差が出やすい作業です。
でも大丈夫、基本をおさえて経験を積めば、デバッグ技術はどんどん向上します。
基本の心得を身につけ、AIも活用して、デバッグ経験を積んでいこう!
コメント