プログラマが知るべき97のこと/他人よりまず自分を疑う


プログラマというものは(つまりわたしたちは)自分の書いたコードに何か誤りがあるとは、なかなか考えようとしない人種です。現に問題が起きていても、それが自分のせいだと思うことは滅多になく、「きっとコンパイラのせいだろう」などと思ったりします。

しかし実際には、コンパイラやインタプリタ、OS、アプリケーションサーバ、データベース、メモリマネージャなど、システムソフトウェアのバグで問題が発生するということは、極めて稀なのです(ほぼないと言っていいでしょう)。もちろんシステムソフトウェアにもバグはあるものですが、責任を押し付けたがる人が期待するよりは、はるかに少ないと言えます。

私が「本当にコンパイラのバグが問題の原因だった」というけいけんをしたのはたった1度だけで、ループ変数の最適化に関わるバグでした。それよりも、コンパイラあるいはOSのバグのせいだとばかり考えて時間を無駄にした回数のほうがずっと多いのです。自分の時間、サポート担当の時間、管理者の時間をさんざん無駄にした挙句、結果は自分のミスが原因だったと判明して力が抜けたということが何度もありました。

多くの人に使われているツール、「枯れた」ツール、様々なテクノロジスタックに採用されているツールは、必然的に品質が上がっています。つまり、何か問題が起きた時にこうしたツールの品質を疑ってもあまり意味が無いということです。もちろん、初期リリースや、世界中でも利用者が少ないツール、めったにダウンロードされないツール、バージョン0.1、オープンソースのソフトウェアなどであれば疑う意味はあるかもしれません(商用ソフトウェアのαバージョンなども同様です)。

コンパイラにバグがほぼ無いのだとしたら、コンパイラの誤りを証明するよりも、自分のコードのバグを見つけることに時間とエネルギーを注いだほうがずっと良いということになります。自分の書いたコードを疑い、デバッグに関して一般に「すべき」と言われていることをやってみましょう。具体的には、問題箇所を切り分け、呼び出し先をスタブに置き換え、テストを書いてみる、といったことをまず行います。呼び出しの作法、共有ライブラリ、バージョン番号のチェックも忘れずに行いましょう。作業内容をチームの他のメンバーに説明してみましょう。スタック破損や変数の型の不一致を見張りましょう。マシンやビルド設定を変えて、例えばデバッグ用、リリース用両方の環境で実行してみましょう。

このとき重要なのは前提条件を考えることです。何を当然とみなすかは人によって違っています。自分と他人では違っているでしょう。たとえ同様のツールであっても、提供するベンダが違えば前提条件は違っているでしょうし、たとえ提供するベンダは同じでも、違うツールならば、やはり前提条件が違っていると考えられます。

問題発見の報告を受けたが、それを自分で再現できない場合には、報告者が実際にどういうことをしているのか、その現場を見るべきです。ひょっとすると、あなたが想像もしなかったようなことをしている可能性があります。予想とは全く違った発想、違った順番で行動していることがあり得るのです。

私は、何か問題が起きた時には必ず真っ先に自分の書いたコードのバグを疑うことにしています。自分のコードを徹底的に調べて、それでもバグが見つからなかった時、初めてコンパイラのバグを疑い、スタック汚染を調査します、たとえば「トレースコードを加えてみたら、問題箇所があちこちいどうしていくのがわかった」という場合なら、自分のコード以外を疑ってもいいでしょう。

マルチスレッドがらみの問題も厄介なバグの原因になりやすく、マシンの前で叫び声を上げたまま白髪になりかねません。「コードはできるだけシンプルにすべき」と常に言われますが、マルチスレッド環境においてこの言葉は何倍も重要です。スレッド間の整合性に関わるバグを、一般的なデバッグ作業やユニットテストで見つけ出すことは困難です。設計をシンプルにするということが何より大切になるのです。

システムに何か問題が起きて、それをコンパイラやOSのせいにしたくなった時は、シャーロック・ホームズの「完全にありえないことをすべて取り除いていけば、残ったものがいかに信じがたいものでも、事実に違いない」という言葉を思い出すといいでしょう。同じく探偵のダークジェントリーも「あり得ないことを全部排除して残ったものは、どんなに信じがたくても、間違いなく真実だ」と言っていますが、ホームズのほうが通りはいいでしょう。