次の方法で共有


第1章 バグとは

デバッギングは、採用しているプログラミング言語やプラットフォームに関係なく、だれにとっても魅力的なトピックです。エンジニアたちが歓声を上げたり、落胆したり、あるいは絶望するのはデバッグ作業中が一番多いことでしょう。無口で内向的な人ほど、デバッグ中に、異様と思えるほどの感情的な行動に出ます。いったんデバッグ作業を開始すると、多くの人は徹夜作業を余儀なくされます。私はこれまで、突然受話器を取り上げ、「いくつかのUML図を書き上げなければならないから、今夜は帰れない」と家族に電話するエンジニアに出会ったことはありません。しかし、「たいへんだ! デバッグ作業が山ほど残っているから今夜は帰れない!」と電話口で嘆く多くのエンジニアを知っています。

1.1 | バグとデバッギング

バグは最高です。いや、バグを検出する作業はすばらしい、というべきでしょう。私たちは学ぶことが好きだから、この世界に入ったはずです。プログラムの奥深くに潜んでいるバグを時間をかけて探し出す作業は、究極の学習方法です。所有する書籍を机の上に何冊も並べて、バグの正体を突き止めようとした経験は数え切れません。奮闘努力の結果、バグの正体を突き止め、それを修正できたときの喜びはなんともいえません。まさに、やった!、という気分です。もちろん、私たちの製品が顧客の目に留まる前に、バグを見つけることが重要です。顧客の目に留まったバグは、バグではなく、契約上の深刻な問題です。バグがバグであるためには、顧客の目に触れる前に検出しなければなりません。顧客が検出したものは、バグではなく、クレームなのです。

他のエンジニアと比較すると、ソフトウェアエンジニアは2つの点でかなり変わっているといえます。第1点は、ソフトウェアエンジニアは職種としては比較的新しく、建築や電気産業などと異なり、産業としては未成熟の分野で活動していることです。第2点は、製品のユーザーはしぶしぶではあっても、瑕疵を受け入れるようになってきていることです。この傾向は、特にPCソフトウェア分野では顕著です。しかし、バグを見つけたときには、不満を口にしないわけではありません。さらに、これは統計上たいへん興味あることですが、PCソフトウェアのバグを受け入れる人々は、原子炉の設計ミスや医療器具の不具合には決して目をつぶることはありません。PCソフトウェアは私たちの日常生活の一部になりつつありますから、こまれでのような暢気な姿勢はもはや通用しません。他の産業分野と同じように、ソフトウェア分野でも品質保証が必要となるでしょう。そのための法整備も間違いなく進む、と私は判断しています。

バグには注意が必要です。最終的には、2つの面でビジネスにはマイナスとなります。短期的には、ユーザーから多数の問い合わせを受けることになります。問い合わせへの対応は経費も時間もかかります。この間、競合相手は次期バージョンの開発作業を行っているはずです。長期的には、経済原則という神の手が作用し、市場はバグのある製品を滅ぼし、同一機能を提供する別の製品を選択することになるでしょう。つまり、バグを抱えた製品は市場を失ってしまいます。今、ソフトウェアはビジネス上の先行投資の対象ではなく、XMLベースのサービスになろうとしています。このような流れの中では、より品質の高いソフトウェアが要求されます。ユーザーはWebサイトからWebサイトに気軽に移動し、各種のベンダから出荷されているサービスを自由に選択できます。ユーザーにとってはすばらしい時代の到来ですが、私たちにとっては失職時代の幕開けです。確実にいえることは、より高い品質のソフトウェア製品へのニーズが高まるということです。バグを含む製品の出荷は即失業という時代が到来しました。バグ。それは職探しの別名です。職探しはエンジニアならだれも望まない、辛い経験です。

1.1.1 | バグの定義

デバッギングについて解説を始める前に、まずは、バグというものを定義しておくことにします。私はバグを、「バグとはユーザーに苦痛を与えるもの」と定義しています。そして、私はこのように定義できるバグを、次のようなカテゴリに分類しています。

  • 一貫性なきユーザーインターフェイス
  • ユーザーニーズを満たさない仕様
  • 低劣なパフォーマンス
  • クラッシュとデータ喪失

■■ 一貫性なきユーザーインターフェイス

このバグは、バグの中では罪のないバグといえますが、ユーザーインターフェイス設計に一貫性がない場合、プログラムのユーザーは困惑してしまいます。Windowsがこれほどまで普及した最大の理由の1つは、すべてのWindowsアプリケーションは基本的には同じように操作することができるということです。Windows標準からかけ離れたアプリケーションを設計開発した場合、その品質がどれほど優れていたとしても、Windowsユーザーはかなりの負担を感じるはずです。たとえば、MicrosoftのOutlookの検索アクセラレータを思い出してください。地球上のほぼすべてのWindowsアプリケーションでは、Ctrl+Fキーを押すと、[検索]ダイアログボックスが表示され、現在のウィンドウ内でテキストを検索できます。しかし、Outlookの場合、Ctrl+Fキーを押すと、開かれているメッセージを転送する機能が起動されてしまいます。私は既に数年Outlookを使用していますが、現在開かれているメッセージ内でテキストを検索するために、F4キーを使う習慣を身に付けられないでいます。

一貫性のないユーザーインターフェイスの解決策については、『Microsoft Windows User Experience』(Microsoft Press、1999年)に紹介されているアドバイスに従うとよいでしょう。この書籍は、Webサイトからも一般公開されています。この書籍情報では不十分な場合、参考になるMicrosoft純正アプリケーションを探し出し、それを参考モデルにしてみるとよいでしょう。Microsoftは基礎研究も重視していることもあり、その成果を積極的に活用しましょう。

フロントエンドとなるWebアプリケーション開発を担当している場合には、表示するユーザーインターフェイス(UI)の標準モデルは存在しませんから、UI設計はかなり難しい作業になります。ユーザーの視点からWebブラウザを見てみるとわかるように、満足すべきUIが提供されているわけではありません。これは、開発者にとっては、ユーザーインターフェイス設計がことのほか難しい作業であることがわかります。強力なWebクライアントUIを開発する場合、私は、次のような2冊の書籍を精読することを薦めています。まず、Webデザインの定本といわれる、Jacob Nielsen氏の『Designing Web Usability: The Practice of Simplicity』(※1)です。もう1冊の書籍は、きわめて小さな書籍なのですが、Steve Krug氏の『Don't Make Me Think! A Common Sense Approach to Web Usability』です。チーム内の独りよがりな、自称UIエキスパートにぜひ1冊進呈するとよいでしょう。Web UIを設計するときには、ユーザーの負担を考慮することを忘れないようにしてください。シンプルなUIを心がけ、膨大なダウンロード時間を要求するような機能は実装しないようにしてください。User Interface Engineering(http://www.uie.com/)の調査結果によれば、CNN.comが採用しているアプローチはすべてのユーザーに最適であることが判明しました。情報をいくつかのセクションに分け、セクション単位で情報グループを整理したうえで、個々のグループに単純にリンクを張ることにより、ユーザーは目的の情報を効率的に探し出せるようになっています。

※1 『ホームページ・ユーザビリティ.顧客をつかむ勝ち組サイト32の決定的法則』(エムディエヌコーポレーション)

■■ ユーザーニーズを満たさない仕様

ユーザーの期待を裏切る製品は、最も厄介なバグの1つといえます。この種のバグは、プロジェクトの開始時に発生するのが普通です。つまり、顧客の実際のニーズを十分に調査していない場合、必ずこのバグが発生するといえるでしょう。製品(シュリンクラップ)であっても、社内向けプログラム(インハウスIT)であっても、この種のバグはコミュニケーション不足にその根を持っています。

一般的には、開発チームが直接ユーザーとコミュニケーションをとることはありません。このため、開発者はユーザーニーズを理解していない、といってもよいでしょう。理想的には、エンジニアリングチームのすべての構成メンバーは、客先を訪問し、自分たちの製品がどのように使われているのか、実際にその目で観察すべきです。1人1人のユーザーの肩越しに、目前に展開される風景を観察すれば、ユーザーが実際には何を必要としているかを理解できるはずです。このような経験を積み重ねていけば、製品設計自体が重要であることも認識できるようになるでしょう。ユーザーと意見交換する場合には、可能な限りより多くのユーザーの意見を聞くようにするとよいでしょう。本音を言えば、本書を読むのを直ちに中止し、ユーザーとのミーティングを設定してほしいくらいです。ユーザーの意見に耳を傾けるのはたいへん重要なことなのです。より多くのユーザーの意見を聞けば、皆さんの設計能力はよりいっそう向上します。

また、サポートセンターに寄せられる電話問い合わせや電子メールの内容を検討してみるのも1つの手です。チーム内の開発者はそのような生のフィードバック情報から、ユーザーが実際に直面している問題を理解することができます。

製品に対するユーザーの期待が大きすぎる場合、その製品は、結果的にバグを抱えている、とみなされてしまうのも事実です。多くの場合、過大な期待は誇大宣伝の結果ですから、過大な製品宣伝活動は控えるべきです。期待した機能が実装されていないことがわかれば、ユーザーは、実際にはそうではなくとも、「この製品はバグっている」と文句を言うものです。提供できない機能を、さも実装しているかのように約束するのは避けるべきです。約束した機能を確実に実装することが重要です。

■■ 低劣なパフォーマンス

ユーザーは購入したアプリケーションをインストールし、実行を開始するわけですが、このとき、アプリケーションの動作が遅いと、彼らはかなりいらいらします。このようなパフォーマンスバグの原因は、動作テストにその根を持つのが一般的です。開発段階ではすばらしい動作結果が出ていても、動作テスト自体に問題があれば(たとえば、実世界の要求を反映していないテスト)、ユーザーのニーズを満たすことなどできません。私は、NuMegaのBoundsChecker 3.0のプロジェクトに参加していました。そのプロジェクトでは、最初のFinalCheckテクノロジにバグを抱えていました。当時のFinalCheckは、BoundsCheckerからエラー情報をレポートできるようにするために、デバッグ情報とコンテキスト情報を直接ソースコードに挿入していました。残念なことに、BoundsChecker 3.0を出荷する前に、私たちは現実的な動作テストを行いませんでした。その結果、私たちの予想に反して、FinalCheck機能を使用するユーザーはわずかにとどまるありさまでした。その後もバージョンアップを繰り返し、FinalCheckを全面的に書き換える努力を惜しみませんでしたが、支持を得ることはできませんでした。初期バージョンのパフォーマンス問題が影を落としていたのです。改善されたFinalCheckは、BoundsCheckerの強力かつ便利な機能の1つにまで成長していたのですが。

パフォーマンスバグを取り除く方法は2つあります。1つは、アプリケーションがクリアすべきパフォーマンス条件を事前に決めておくことです。パフォーマンス問題が存在するかどうかを評価するために、目標を設定しておき、それをクリアしているかどうかを測定するとよいでしょう。条件定義時には、基本となるパフォーマンスを数値化しておくことが重要です。測定値がいずれかの目標数値を下回っている場合(10%程度が目安)、作業を中止し、原因を特定する作業を始めるとよいでしょう。もう1つは、可能な限り実際のユーザー環境を想定したうえで、テストを繰り返すことです。このテスト作業は、できる限り開発工程の初期の段階で行うとよいでしょう。

1つの例を紹介しておきましょう。私はこの数年、多くの開発者から「パフォーマンステストを行いたいのですが、必要なデータをどのように集めればよいでしょうか?」というような質問を受け取っています。テストで使用するデータは、実世界を反映したものである必要がありますから、この質問への回答は「ユーザーから集めなさい」です。プログラムを実際に使用するユーザーの意見を聞き、そこからテストデータを掘り出すのです。掘り出したデータが意味のあるものであることを、直接そのユーザーに確かめるのも重要です。何よりもコミュニケーションが大切です。ユーザーがプライバシ問題を心配しているような場合、重要情報を変更する単純なプログラムを作成してみるとよいでしょう。そのプログラムを実際に実行してもらい、情報の変更によりプライバシ問題が一切発生しないことを目で確認してもらうのです。プライバシデータを入力しても安全なことを実際のプログラムで証明して見せるのです。可能なら、簡易ソフトウェアを無料で提供し、実際に試してもらうのもよいでしょう。ユーザーからデータを集めるには、ユーザーとのコミュニケーションが大切なのです。

■■ クラッシュとデータ喪失

ほとんどの開発者とユーザーは、バグという言葉を聞けば、クラッシュとデータ喪失をイメージするでしょう。私は、メモリリークもこの種のバグの仲間に数えたいと思います。

ユーザーは、これまでに紹介したようなバグが発生したとしても、そのまま作業を継続できる可能性がありますが、クラッシュが発生した場合には、すべての作業をその場で中断しなければなりません。本書の以降の章の多くは、このような深刻なバグの解決策を取り上げています。また、クラッシュとデータ喪失は、一般的に発生するバグであることも事実です。ご存知のように、このようなバグのいくつかは簡単に解決できますが、ほとんど解決できないような種類のバグもあります。重要なことは、この種のバグを抱えている製品は絶対に出荷してはならないことです。これは肝に銘じるべきです。

1.1.2 | プロセスバグとソリューション

注意を怠らなければ、バグのないソフトウェアを出荷することは可能ですが、私の経験から言えば、すべての開発チームがそのようなレベルに達しているとは思えません。バグはソフトウェア開発では避けて通れない、といってもよいと思います。しかし、開発中のアプリケーション内のバグの数を、最小に抑えることはできるはずです。また、そのような努力をしている開発チームは多数存在します。バグの発生原因は大まかに、次のようなカテゴリに分類できます。

  • 厳しすぎるスケジュール
  • コーディング先行型開発
  • 不完全な要求定義
  • 技術的な無知
  • 低いモラル

■■ 厳しすぎるスケジュール

私たちはチームの一員として開発作業に参加します。そして、開発スケジュールは、チームの「管理者」がいろいろな事情を考慮して決定しています。非現実的なスケジュールが設定された場合、私たちは管理者たちを責めたいところですが、実際には彼らに責任はありません。通常は、エンジニアのスケジュール見通しが、実際の開発スケジュールのベースになっています。時には、エンジニアの見通しが間違っていることがあります。たとえば、立派な製品を開発するために必要な作業時間を軽く見ていたりしています。エンジニアはどちらかといえばおもしろい人種です。彼らは自己中心的で、独りよがりなところがありますが、物事を前向きに考える点では共通しています。仕事が与えられれば、彼らはコンピュータの電源を入れ、勇んで作業します。管理者がXML変換機能をアプリケーションに追加するように要請すれば、平均的なエンジニアは、「承知しました。3日もあれば大丈夫です」と請合うはずです。もちろん、そのエンジニアはXMLの知識を持たないかもしれませんが、作業期間が3日必要であることは理解しているのです。しかし、このやり取りでは、エンジニアも管理者も必要な知識を学習する時間を考慮していません。第2章の「2.2 デバッグシステムの構築スケジューリング」では、スケジューリング時の考慮事項をいくつか取り上げます。現実的でないスケジュール設定は、製品品質の低下に直結します。スケジュールを設定した人がエンジニアか管理者かなどは、問題ではありません。スケジュールを守るために、必要な機能は削られ、必要なテストも行われなくなる、ということが問題なのです。

私が所属したいくつかの開発チームは、スケジュール通りに製品を出荷することができました。たいへん幸運だったといえます。そのようなチームは、例外なく自分たちでスケジュールを完全に掌握していました。私たちは、現実的な出荷日を設定できる能力を備えていたわけです。出荷日を設定する際、私たちは機能セットを洗い出し、それをベースに日程を立てていました。会社側が私たちの算出した作業時間を受け入れない場合、一部機能を削り、日程を調整しました。また、管理者側に日程表を提出する前に、開発チームの全員の同意を得るようにしました。このため、チームの全員が、製品をスケジュール通りに完成させようと団結できたわけです。これは、スケジュールだけを問題にしているようですが、チーム内の意志の疎通の重要性も訴えています。このようなチームの開発製品は、実は品質も高いのが一般的です。

■■ コーディング先行型開発

プロジェクトチームによっては、プログラムの機能を定義する前にコーディングを始めてしまいます。友人のPeter Ierardiは、そのような開発姿勢を「コーディング先行型開発」と呼んでいます。程度の差こそあれ、私たちはこのようなアプローチを取っています。ソースコードを書き、それをコンパイルします。また、必要に応じてソースコードを書き、デバッグします。このような作業は本当に楽しいものです。だから、皆さんも私もこの業界にいるのです。椅子に座り、これから開発するプログラムの機能を書き留める作業を好むプログラマはめったにいません。

しかし、嫌いだからといって、プログラム機能の定義を怠ると、バグの世界への窓を開くことになります。事前にバグの発生原因を取り除く努力をするのではなく、多くの人は発生したバグを「とりあえず」回避する方向で作業します。ご想像のように、この戦略は問題を二重三重に複雑化します。元々のコードが安定性を欠いているわけですから、その上にコードを追加すれば、新たなバグがさらに発生するのは当然の理です。私が勤務していた会社は、世界中の顧客に対してデバッグサービスを提供したり、デバッグツールを販売していました。データ喪失やパフォーマンス低下問題へのソリューションを求められることが多かったのですが、不幸なことに、私たちのできることはありませんでした。多くの問題は、基本アーキテクチャにその根を持っていたからです。依頼してきた会社の管理者層に問題を説明し、部分的な書き直しが必要であることをアドバイスすると、「このコードベースには、多くの時間と資金を投資しているのだから変更などできない!」という反応が返ってくることがありました。そのような反応が返される場合、コーディング先行型開発という症状の兆しが現われていると考えて差し支えありません。管理者層への最終報告書には「CFTL(Code First Think Later)」とだけ書き込みます。

さいわい、この種の問題へのソリューションを見つけるのは簡単です。まず、プロジェクト計画をきちんと練ることです。要求定義やプロジェクト計画に関しては、優れた文献が出版されています。参考となる文献は付録に整理してあります。後日、一読するとよいでしょう。決しておもしろいものではないかもしれませんが、バグ発生を防止するには事前設計が重要な意味を持つことがわかるはずです。

『Debugging Applications』(※2)では、プロジェクト計画の必要性を強調しただけで、計画の実際の練り方を説明しませんでした。一部の読者から不満のメールを頂戴しています。その不満は理にかなったものですから、本書では詳しく説明することにします。正直に言えば、計画の練り方に正解はありません。少なくとも私は知りません。言い訳を言っているように聞こえるかもしれませんが、ちょっと待ってください。計画の練り方に正解はありません。これは確かです。しかし、私なりの練り方はあります。私の練り方が正しいかどうかはわかりません。それでは、私の練り方を説明します。

※2 邦訳『Windowsプログラマのためのデバッグテクニック徹底解説』(日経BPソフトプレス)

巻末の著者紹介を読めばわかりますが、私は20代後半になってからソフトウェアビジネスの世界に入りました。実は、ソフトウェアビジネスは2番目の職業です。最初の職では、飛行機から飛び降り、敵を探し出して攻撃を加えるという任務を課されていました。私は、米国陸軍の降下兵であり、また特殊部隊(グリーンベレー)の一員でした。ソフトウェアビジネスと関係がないように見えますが、決してそのようなことはありません。まさに、ソフトウェアビジネスを始めるための準備だったのです。実際に私に会ってみればわかりますが、度の強いメガネをかけた中年太りの男です。しかし、かつては勇敢な戦士だったのです。そう、空中を舞う戦士だったのです!

特殊部隊の一員として、私は計画の練り方を学びました。任務が特殊であり、落命する確率が高い場合、可能な限り慎重に計画を練ることになります。任務を遂行するための計画を立てる場合、米国陸軍は参加チームを「隔離」状態におきます。特殊部隊の本部のあるFort Bragg(Carolina州北部の都市)には特別な地域があり、部隊はそこに隔離されます。外部との接触は当然禁じられます。計画段階では、「落命する汝の姿を想像せよ!」というキーワードが使われていました。私たちは車座になり、シナリオを考えたものです。降下地点を忘れ、空軍がそれを見つけ出すことができなかった場合、私たちの運命は? 降下する前に負傷者が出た場合、その後の計画はどうする?

地上に降下後、合流する予定になっているゲリラ部隊の指揮官を見つけることができない場合、どう行動する? 合流予定になっているゲリラ部隊の指揮官が予想以上の部下を連れてきた場合、どう対処する? 強襲されたときの備えは? 私たちは隔離を解かれるまで、疑問点を延々と出し合っては、その回答を見い出す作業に追われました。つまり、考えられるすべての条件を洗い出し、条件に応じた方針を打ち立てたのです。条件の見落としは落命です。そのとおりです。落命する確率が高いことがわかると、人というのは全神経を使って、落命をもたらす原因を必死で探し出すものなのです。

ソフトウェアビジネスでも軍の経験が大いに役立っています。最初の頃は、「要求を整理する前に、Bobが死んだらどうなる?」などという発言を繰り返していました。もちろん、プロジェクトのメンバーは目を丸くしていましたから、表現を柔らかくし、「要求を整理する前に、宝くじに当たってBobが会社を去ったらどうする?」と質問するようにしてきました。表現が単に軟らかくなっているだけで、その本質は変わりません。計画内の疑問と混乱を洗い出し、適切に処理します。これは決して容易な作業ではなく、意志の弱いエンジニアは弱音を吐くことになります。しかし、疑問点への回答を粘り強く探していくうちに混乱は解消され、キーポイントが浮かび上がってきます。たとえば、要求定義時には、「定義した要求がユーザー要求を満たしていない場合、どうする?」という質問が出てきたりします。この質問への回答作業は、時間と経費をかけて行うべきです。設計段階では、「パフォーマンスが十分でない場合、どうする?」という質問が出るものです。この質問により、私たちは目標となるパフォーマンスを設定し、実際のユーザー環境に近いテスト環境を整備しながら、開発作業を進めるための計画を練り上げることになります。すべての問題点がはっきりしているのであれば、計画を立てることはたやすいものです。しかし、私たちの現実は納期に縛られています。すべての問題点を明らかにする、時間的な余裕がないのが現実です!

デバッグ物語

最悪なCFTL

■発生したバグ

あるクライアントから電話が入り、「納期が迫っているのに、パフォーマンス問題を抱えている!」と助けを求めてきました。私たちはとりあえず、15分ほどのシステムレビューをお願いしました。基本的なアーキテクチャ概要さえ把握できれば、プロジェクトの性格や発生している問題の種類をある程度予測できると考えたからです。クライアントは私たちの要求を受け入れ、1人のアーキテクトを割り当ててくれました。彼はホワイトボード上に図を描き始めました。

一般的には、このようなレビューセッションは10分から15分で終了するものです。ところが、担当者は45分経過しても、円や矢印を盛んに描いている状態でした。私はロードマップを必要としていませんでした。メンバーはいらいらし、ついに立ち上がって、「そういうことではなく、10分程度でシステム概要を説明してくれないか!」と要求しました。私は詳細を知る必要はなかったのです。必要なのは、システムの論理的な構造だったのです。アーキテクトは、また説明を始めました。15分後、私は彼らのシステムの25%を理解することができました。

■デバッグ作業

このシステムは巨大なCOMシステムでした。システムの25%程度を理解しているに過ぎませんでしたが、私は、パフォーマンス問題を引き起こしている原因がなんとなく見えてきました。プロジェクトチームのアーキテクトは、COMの虜になっていました。その人はCOMの虜であり、COMの利点を活かす人ではありませんでした。彼は、COMの利点を活かそうと考える前に、COMのすばらしい機能を使ってみよう、と考えたのです。少し時間が経過してから予測できたことなのですが、そのシステムでは8個から10固程度のメインオブジェクトを作成すれば十分でした。しかし、プロジェクトチームは、80個を超えるオブジェクトを作成していたのです。これはまったく信じられないことです。文字列内のすべての文字をCOMオブジェクト化しているようなものです。COMの虜になるのはわかりますが、プロジェクトには計画が必要です。プロジェクトのアーキテクトは、この面での実務経験がありませんでした。半日過ぎた頃、私は管理者を部屋の隅っこに呼び出し、手の打ちようがないことを静かに伝えました。COMのオーバーヘッドがパフォーマンス問題を引き起こしているからです。管理者は、このようなアドバイスを聞きたくありませんでした。彼は、あの有名な文句を大声で繰り返しました。「このコードには巨額の投資をしているんだ。今から変更することなどできない!」。残念ながら、アーキテクチャを変更しない限り、パフォーマンスは改善できません。これが私たちの結論でした。

■教訓

このプロジェクトは、開始時からいくつかの問題を抱えていました。すべての問題は小さなものではありません。まず、チームメンバーは、設計というものを軽視していました。実装とは設計の実装です。設計がなければ、実装はできません。もう1つの問題は、計画を練り上げる前に、コーディングを開始したことです。コードを書き、その一部を修正し、またコードを書いている状態でした。そこには、論理的な計画は存在しませんでした。つまり、設計なき、コーディング先行型開発の典型だったのです。テクノロジを評価する際には、その現実を理解することが重要です。そして、コンピュータの電源を入れる前に、開発プロセスの計画を練ることが重要です。

■■ 不完全な要求定義

周到な事前設計は、機能連鎖という、深刻なデバッグを発生させる原因を最小に食い止めることができます。機能連鎖というのは、当初の計画にはない機能が入り込む事態を指します。これも一種のバグですが、その根は中途半端な設計や精度を欠いた要求定義にあります。競合相手からのプレッシャや管理者の気まぐれな希望により、出荷直前に機能を追加するようなことも起こり得ます。このような場合には、ソフトウェアにバグが忍び込むと考えるとよいでしょう。

ソフトウェアエンジニアリングはアバウトな作業ではなく、きわめて細かな作業を要求します。コーディングを開始するために詳細を洗い出し、必要な対処法を用意しておけば、それだけバグが発生する余地は少なくなります。可能な限り詳細に目を配るには、プロジェクトの要求とその実装を事前に計画する以外にありません。もちろん、机に張り付き、計画詳細を記す何千ページにも及ぶドキュメントを作成することではありません。

私がこれまで作成してきたデザインドキュメントの中で最も優れていたのは、ユーザーインターフェイスのラフスケッチ(プロトタイプ素描)でした。Jared Spool氏と同氏の会社(User Interface Engineering)が収集した調査データと報告を参考にしながら、私のチームはユーザーインターフェイスの素描を作成し、各ユーザーからの要求を、その素描を基に完全に解決しました。私たちは製品要求に焦点をおき、ユーザーがどのような手順で作業を行うかを正確に定義しました。最終的には、製品に実装する機能を正確に理解できました。そして、これがより重要なのですが、社内のすべての人間も製品機能を同じようにきちんと理解していました。ある特定の状況下で何が起こるかわからない場合、素描を取り出し、それを基にして適切な回答を見つけ出すようにしました。

完璧な計画を作成していたとしても、それを実行に移すには製品要求を理解しておく必要があります。私がかつて勤めた会社は、幸運にも1年未満で脱出できましたが、製品要求定義はきわめて単純であり、わかりやすいように思えました。しかし、ほとんどの開発チームメンバーは、ユーザーニーズを理解していなかったため、製品に実装すべき具体的な機能を定義できませんでした。その会社は、エンジニアリングスタッフを日々劇的に増員していましたが、新規に採用したエンジニアに十分な教育を行ってはいませんでした。それまでの開発会社と同じように、経営上の間違いを繰り返していたわけです。このため、開発チームが詳細な計画を作成したとしても、製品出荷は2、3年遅れ、市場に受け入れられることはありませんでした。

このプロジェクトには2つの大きな誤りがあります。第1の誤りは、会社がきちんと時間を取って、ユーザーニーズを新規に採用したエンジニアに十分説明していないことです。私たちは、そのような教育を行う必要性を強く訴えたにもかかわらずです。第2の誤りは、解決しようとしている問題を、勤続年数に関係なく積極的に解明しようとするエンジニアも、それほどいたわけではありませんでした。この結果、マーケティング・営業関係者がユーザー要求を説明するたびに、チームは開発の方向性を繰り返し変更していました。コードベースは安定性を欠いていましたから、クラッシュを発生させずに最小のユーザーニーズを満たすために、数か月の時間を要するありさまでした。

プロジェクトの問題領域を参加エンジニアに説明する体制を整えている会社は、それほど多くありません。エンジニアの多くは、それなりの専門教育を受けていると思いますが、ユーザーが自分たちの製品をどのように使用するかについては、それほど知らないのが普通です。このため、会社側が目の前の問題領域を、きちんとエンジニアに説明することが必要といえます。エンジニアに適切な製品知識を与えれば、エンジニアたちは製品要求を理解できるようになり、最終的には多くのバグを取り除けることになります。

しかし、一方的に会社側を非難するだけでは不十分といえます。エンジニア側も当然責任から逃れることはできません。彼らは、製品要求を理解する積極的な姿勢を取る必要があるからです。エンジニアの中には、自分たちはソリューションを見い出すためのツールを開発しているのだから、自分たちの作業は問題領域から切り離されるべきである、と考えたがる人がいます。私たちは、エンジニアであることを思い出す必要があるでしょう。私たちは、ソリューションを見い出す手助けをするのではなく、ソリューションそのものを見い出す仕事人なのです。

ソリューションを見い出すというのは、次のようなことを意味します。技術的には、問題のないユーザーインターフェイスを開発したとします。しかし、そのインターフェイスが、ユーザーのニーズを満たしていないならどうでしょう。これはソリューションではなく、ソリューションを見い出すための手助けをしているにすぎません。また、目の前のユーザー問題を解決するアプリケーションを開発できたとしても、ユーザーのビジネス戦略の変更に柔軟に対応できないなら、そのアプリケーションもソリューションではなく、可能性を提示しているにすぎないといってよいでしょう。

可能性を示すのではなく、ユーザーの問題を解決できるとき、私たち開発者は製品要求を理解するナレッジワーカーとなるのです。このようなエンジニアの製品は、間違いなくユーザーの強力な手足となります。優れたエンジニアは、開発技術をいじくり回すのではなく、それを使ってユーザーの問題を解決できるナレッジワーカーなのです。

■■ 技術的な無知

開発者の無知もバグ発生の原因になります。開発者がプロジェクトで使用するOSプラットフォーム、プログラミング言語、テクノロジなどに関する十分な知識を持っていない場合、彼の担当するコードにはバグが入り込む可能性があるといえます。残念なことに、エンジニアは知識不足を認めようとせず、トレーニングの必要性を自ら申告することもありません。彼らは知識のなさをひた隠し、意図的ではないにしても、バグを製品内に埋め込んでくれます。技術不足は必然的にバグを発生させます。

しかし多くの場合、技術力不足は個人的な問題ではありません。先端ソフトウェア開発分野の本質的な特徴といってよいでしょう。つまり、先端OSは新たな層が幾重にも追加され、相互に複雑な依存関係を持っています。今日のソフトウェア開発は、このような複雑な層への理解を要求しています。はっきり言えば、これは個人の能力を超えています。より単純に表現すれば、すべてのOSに精通したうえで、プログラミング言語と最新テクノロジ層を理解することなど何人にとっても不可能なのです。「その分野に関してはよくわかりません」と告白することは、決して罪ではありません。実際、健全なチームなら、個々のメンバの長所と短所を公にしています。これは最終的にチーム開発に貢献します。チームを構成する各開発者の長所と短所を分類し、日々弱点を補うことにより、最大のチーム効果を上げることができるようになります。スキルが不足していることがわかっていれば、それを向上させることができます。個々の開発者の弱点を改善すれば、そのチームは予測できない状況が発生しても、柔軟に対応できるようになるでしょう。つまり、最終的には、チーム全体のスキルが向上することになるのです。また、個々の開発者の弱点がはっきりすれば、学習時間を加味したうえで、より正確な開発スケジュールを設定できるようになります。

最新技術を学ぶ最良の方法は、それを実際に使って何かをしてみることです。NuMegaに在籍していた数年前、私は社命により、Microsoft Visual Basicを学び、Visual Basic開発向けの支援ツールを作成する機会がありました。そのとき、私はまず学習予定スケジュールを作りました。それを上司に見せると、彼は驚いていました。予定では、The Insulterというアプリケーションを作ることにしました。Version 1は、1つのボタンを持つ極めて単純なフォームでした。ボタンを押せば、プログラム内に定義された、人をからかうような文字列をランダムに表示しました。Version 2は、文字列をデータベースから読み出すとともに、ユーザーが文字列をデータベースに追加できる機能を搭載していました。Version 3ではさらに改良を加え、Microsoft Exchange Serverに接続し、社内の同僚に文字列を電子メールで送信できるようにしました。直属の上司は驚きながらも、たいへん喜んでいました。提出したスケジュールを見れば、私の学習内容をはっきり理解できるからです。上司というのは、私たちの日々の業務内容を自分の上司に報告します。それが彼らの仕事なのです。このため、直属の上司にスケジュールを提出しておけば、皆さんは会社にとっては貴重な人材ということになります。私が初めて.NETに触れたとき、私はThe Insulterを思い出し、.NET版Insulterを作ってみようと思い立ちました。

開発者が備えるべき技術や知識のより詳細については、「1.2 デバッグ作業と前提知識」で取り上げます。

よくある質問

コードをレビューする必要があるでしょうか?

もちろんです! 残念なことに、多くの会社は完全に間違った認識を持っています。以前勤めていたある会社は、コードレビューを必須と定めていましたが、それは形式的なものでした。そのやり方は、私が学生時代に読んだ1冊の書物をまねたものであり、非現実的でした。体裁を整えているだけだったのです。コメントを記録する人、会議を進行する司会者、ドアを開けるドアマン、酸素を吸うリーダーなどがきちんと決まっているだけでした。完璧なまでに意味のない、ロールベースだったのです。部屋には40人ほどいましたが、コードを読む人はだれ1人いませんでした。まったくの時間の浪費です。

私が好ましいと考えるコードレビューは、形式を重んじるのではなく、1対1の対面ベースです。コードのハードコピーを一部受け取り、担当者と1行1行検討していきます。愚直に検討しながら、入力と出力の対応関係をチェックします。このチェックにより、コード内でどのようなことが起こり得るかを理解できます。これはきわめて原始的ですが、ちょっと考えてみてください。それではデバッグと同じではないか? と考えた人もいるはずです。まさにそのとおりです。コードの動作を正確に検証するのです。これこそコードレビューです。

また、ベテラン開発者のコードを、それほど経験のない開発者にレビューさせることもよいと思います。スキル向上に寄与するだけではなく、製品知識と活きたプログラミングノウハウを身に付ける絶好の機会となります。

■■ 低いモラル

これは私の意見ですが、プロジェクト内にバグが入り込む最後の原因は、これから紹介する開発者のモラルです。そして、この原因が最も深刻といえるでしょう。私がこれまで話をしたすべての企業とエンジニアは、品質の重要性を口々に唱えています。しかし残念なことに、いくつかの会社と数人のエンジニアは、実際にはそのような認識を持っていません。つまり、口先だけでした。品質を本当に重視する会社やエンジニアと作業した経験を持っている人は、彼らが持っているモラルの真の意味を理解しているはずです。彼らは、自分たちの製品に強い誇りを持ち、製品の隅々まで注意を払い、改善のための時間を惜しみません。たとえば、彼らはアルゴリズムを複雑にするのではなく、より単純化し、それをテストするための最良の方法を、時間をかけて編み出しています。顧客はアルゴリズムを購入するのではありません。ユーザーは品質の高い製品を購入するのです。品質を重視する会社と個人は、似たような特徴を持っています。慎重な事前設計、優れた説明能力、確かな品質管理、そして優れたコミュニケーション能力などは彼らに共通しています。大規模なソフトウェア開発(スケジュール管理、分析/設計と実装管理など)を経験している企業と個人は多数存在しますが、納期を守りながら、高品質の製品を出荷できる会社と個人の数は極めて限られてます。

開発モラルの具体的な優れた例を紹介しましょう。私は、NuMegaでは月例レビューというものを行いました。最初のレビュー時、私はたいへん驚かされました。通常は、管理者からのフィードバックを待つ必要がありますが、レビューはあっという間に終わってしまったのです。そのレビューの目的の1つは、自分が参加した開発製品に関して、どのくらいの数のログを取っているかを整理することでした。NuMegaは、このような統計記録を私の仕事の一部として評価していたのです。私は、その事実を知ったときたいへん驚きました。バグ追跡は、製品品質を向上させるうえでは重要な意味を持っています。しかし、それまで勤めた会社は、そのような方針をまったく持っていませんでした。開発者は、バグがどこにあるかを知っていますが、バグ追跡システムにそれらを記録する経済的なメリットがありません。つまり、インセンティブが与えられていないのです。NuMegaは、このような開発者の心情を理解していたわけです。バグ入力件数が自分のレビューの一部であることを知ったとき、私は検出したすべてのバグを記録することにしました。どのような些細なバグでも記録しました。テクニカルライタ、品質管理エンジニア、開発者、管理者が先を争うようにバグを記録するようになれば、バグがユーザーの元に到着するようなことはありません。そしてより重要なことは、チームのすべての参加者は、自分たちのプロジェクトが今どこまで完成しているのかを、はっきりと理解できるようになることです。

エンジニアリングの視点から見ると、『Debugging Applications』も優れた例の1つです。付属CD-ROMには、コンパイルされていないソースコードだけでも、2.5MBを超えるデータが収録されていました。これは、平均的な書籍に収録されているデータ量の数倍になることでしょう。多くの人は気付いていないと思いますが、サンプルコードの開発時間の半分以上はテストに費やしていました。私は、Bugslayerコード内にバグを見つけた人が、興奮して「バグあり!」という電子メールを送信してくるのをひそかに待ち望んでいました。バグはない、と断定することはできませんが、私が知る範囲では5つだけです。つまり、私は、コード開発には持てる能力のすべてを使い切ったのです。本書のコード量は6Mほどに増えていますが、私の目標は、バグの数を5個未満にすることです。

私が開発管理者をしていたとき、プロジェクトの全員が品質へのこだわりを持つような雰囲気を作り出しました。つまり、すべてのチームメンバーに対して、節目節目で、自分たちの開発製品内にミスが出ないように圧力をかけたわけです。これにより、自分たちの製品を自信を持っていつでも出荷できる、という意識が芽生えます。もちろん、彼らにはその意味の重要性を理解してもらいました。チームのだれかが、製品はまだ完成していないと感じれば、当然出荷しませんでした。出荷するのではなく、まず主要なバグを取り除き、辛いテストを繰り返しました。自分たちが自慢できる製品のみを出荷すべきであると考えました。このような私の管理姿勢は、開発者間に品質へのこだわりを生み出すとともに、自分たちの開発製品への誇りも与えました。たいへん興味あることに、だれかのバグにより製品の出荷が遅れる、という現象は発生しませんでした。バグは、それを発生させた本人がきちんと修正していたのです。チームの全員が自立していたのです。

会社自体が品質を重視する方針を打ち出せば、社内の開発者全員のモラルは確実に向上します。このような方針は、新人採用段階からはっきり打ち出す必要があります。そして、製品候補版(RC)を出荷するまでのすべてのプロセスで、徹底しておく必要があります。「当社は品質を何よりも重視する」という姿勢を確実に浸透させることが重要です。優秀な技術者を採用したい、とはどの会社も主張しますが、そのような人材を確保するための給与体系やインセンティブを用意していないのが現状です。また、会社によっては、高品質の製品を出荷するために必要なエンジニアリングツールや装置を購入するのを嫌がっています。残念なことに、解決の難しいクラッシュバグを数分で退治できる、500ドルのツールの購入を嫌がる企業が多すぎます。500ドルのツールを購入しないばかりに、このような企業はバグ検出とその修正のために数千ドルを浪費しています。その多くは人海戦術にかかる人件費です。

会社によっては、従業員の解雇を通して品質重視を鮮明にします。つまり、会社側が定めた基準を遵守しない開発者は職を失うのです。これは当然のことですが、解雇することはどの会社にとってもつらい決断です。優れた開発チームを作り上げるには、優れた開発者を手放すわけにはいきません。仕事もせずに高収入を得ている開発者がいます。自分は深夜残業を繰り返し、時には週末も作業している場合、馬鹿らしい、という気分になってしまいます。このような社風では、優秀な開発者は育ちません。優秀な開発者が育ったとしても、他社に転職されてしまうでしょう。

プロジェクトの管理者をしていたとき、私は我慢に我慢を重ねましたが、クリスマスの2日前にある人を解雇しました。チーム内の他の開発者は、その人は会社の基準に従って作業していないことをうすうす感じていたと思います。彼らがクリスマス休暇から戻り、あの人がまだチームに残っていたら、おそらく、私は最終的にはそのチームを失ったことでしょう。私は、解雇した開発者の勤務状態をしばらくの間記録していましたから、解雇する準備には怠りなかったといってよいでしょう。もちろん、解雇の決断は断腸の思いでした。波風を立てることなく、やり過ごしたほうが楽だったのですが、しかし、チームのためだったのです。私を雇用した会社は、品質を重視していたのです。解雇の決断は、チームと会社のためにやらねばならない、私の仕事だったのです。最終的にチーム内の3人を解雇しました。チームの生産性を落とすよりは、大鉈を振るったほうがよいのです。好んで解雇を言い渡す人はいません。私にとっては、やらなければならないことだったのです。品質を維持することはたいへんなことです。深夜残業の繰り返しは、高品質のソフトウェアを出荷するための代償です。管理者には、チームを管理するというつらい仕事も待っています。

皆さんの中には、現在所属している組織には品質へのこだわりがまったくない、と感じている人もいることでしょう。もし、それが事実なら、その雰囲気を一夜で変える手はありません。品質を重視する社風は、一夜にして生まれることはないのです。もし、皆さんが管理者の立場であるなら、自分の下で作業しているエンジニアのために方向性を打ち出し、より良い雰囲気を作り出すことは可能なはずです。そして、皆さんの上司に働きかけ、会社全体にその雰囲気が浸透するように努力してみるとよいでしょう。1人の開発者として勤務している場合には、堅牢でしかも拡張性に富むコード開発を心がけ、他の開発者の模範となるとよいでしょう。

1.1.3 | 計画的なデバッグ

これまでの説明から、バグの種類とその発生原因は理解できたかと思います。また、そのようなバグを回避する方法と修正する方法も、かなり理解してもらったと思います。ここでは、デバッギングプロセスそのものを取り上げます。話が1段階進むことになります。多くの人は、コーディング段階でクラッシュが発生すると、その時に始めてデバッギングについて考え始めます。しかし、バグの根はコーディングではなく、プロジェクト開始時にあることを忘れるべきではありません。つまり、アプリケーションにどのような機能を実装するかを考えている段階、いわゆる要求定義段階に問題が潜んでいるのです。顕在化しないだけです。プロジェクトの事前計画をしっかり行えば、デバッグに要する費用も時間も少なくなります。

既に述べたように、機能連鎖はプロジェクトに実害を与える原因になります。多くの場合、実装機能定義を無計画に行った場合、バグが入り込み、プロジェクトの進行に支障をきたします。これは何も、計画詳細を明文化することを要求しているのではありません。時には、プロジェクトの進行途中で競争力を高めるために、機能の一部を変更したり、あるいは新規機能を追加することもあります。また、ユーザーからの要求により、効率的に対応できるように機能の一部修正や追加を行うことはよくあります。重要なことは、コードを変更する前に、変更する内容を定義することです。計画をきちんと整理することです。さらに、機能を追加したことにより、コード全体が影響を受けることは好ましいことではありません。このような影響は、テスト、ドキュメント、時には、宣伝文句自体の変更を避けられなくなります。開発スケジュールを見直すときには、1つの機能を追加/削除すると、プロジェクトを完了するための時間が幾何級数的に増える、ということを理解しておく必要があります。これが、機能連鎖の背景です。

『Code Complete』(※3)(Microsoft Press、1993年)というすばらしい本があります。これはSteve McConnell氏が執筆した書籍ですが、同氏はその中で、バグフィックスに要するコストを取り上げています。同氏によれば、要求定義やスケジューリング段階でのバグのコストはほとんどかからない、と言っています。しかし、プロジェクトが進行するにつれ、バグのコストは劇的に増大していきます。これはデバッグコストそのものですが、実は、プロジェクトの途中で機能を削除したり、追加したりすれば、同じように莫大な追加コストが必要となります。

※3 邦訳『コードコンプリート』(アスキー)

デバッギング作業の計画は、テスト計画と同時に行う必要があります。計画を作成するときには、デバッギングとテストをスピードアップし、効率化するいろいろな方法を用意しておくとよいでしょう。1つの方法は必要に応じて、ファイルデータダンプと検証機能を持つツールを開発し、バイナリファイルと内部データ構造体の動作を確認できるようにすることです。プロジェクト内において、バイナリファイルとの間でデータの出し入れを行っている場合、そのデータを判読できるフォーマットで、テキストファイルにダンプするテストプログラムを作成するようにスケジュールしておくとよいでしょう。このようなダンププログラムは、データを検証し、バイナリファイル内のすべての依存関係をチェックするような機能を実装しておくことも忘れないようにしましょう。このようなツールを用意しておくと、テストとデバッグ作業は飛躍的に効率化されます。

適切なデバッグ計画を作成することにより、デバッガ内で過ごす時間を劇的に短縮できます。作業時間の短縮はすべてのプロジェクトの目標です。本書はデバッグを主題としていますから、「デバッグ作業は簡単に切り上げよう」という主張はおかしな響きがします。しかし、バグを避ける努力こそデバッギングの第一歩なのです。アプリケーション内に十分なデバッグコード(デバッガではありません)を組み入れていれば、そのコードはバグの発生位置を教えてくれます。デバッグコードについての詳細は第3章で取り上げます。

1.2 | デバッグ作業と前提知識

デバッギングの本論に入る前に、デバッギングを行ううえで必要とされる前提知識を説明しておきます。すべてのデバッグ専門家は、優れた開発者である必要もあります。事実、多くのデバッグ専門家は、優れた開発者である点で一致しています。立派な開発知識がなくては、効率的なデバッギング作業を行うことなど不可能です。優れたデバッグ専門家は、優れたソフトウェアを開発できるのも事実です。

1.2.1 | スキルセット

デバッグの達人や優れたソフトウェア開発者は、問題を解決するための高度なスキルを持っています。さいわい、そのようなスキルは習得可能であり、さらに自分自身で磨くことができます。優れたデバッグ専門家やプログラマは、基本的な問題解決スキルに加えて、プロジェクトを構成する要素間の相互関係を見通す能力を備えています。この総合力こそ、並みの開発者と優秀な開発者との間の違いです。

並みの開発者ではなく、優秀なデバッグ専門家や開発者になるためには、次に示す分野に精通する必要があります。

  • プロジェクト
  • プログラミング言語
  • テクノロジとツール
  • OS
  • CPU

■■ プロジェクトの理解

参加するプロジェクトの目的を、きちんと理解することが何よりも重要です。プロジェクト内容を理解していれば、ユーザーインターフェイス、ロジック、パフォーマンスに関するバグ発生をかなり回避できます。各種のソースファイルのどこにどのような実装コードが記述されているかがわかれば、だれが何のために何をしているのかをすばやく理解できるようになります。

残念なことに、プロジェクトは一様に定義することはできませんから、存在するのであれば設計書を読み、デバッガ内でソースコードを検討する以外にありません。最新の開発環境はクラスブラウザを備えていますから、それを有効活用し、基本的なクラス構成を把握するとよいでしょう。Source DynamicsのSource Insightなどは強力な市販ブラウザの代表です。また、Microsoft Visual Studio .NET Enterprise Architectには、Microsoft Visioが統合されています。これはモデリングツールであり、コード内のクラス間の関係を示すUML(Unified Modeling Language)ダイアグラムを作成してくれます。逆アセンブルリストを時間をかけて解読するよりも、粗末ではあっても、ソースコードを検討することを忘れないようにしましょう。ないよりはましでしょう。

■■ プログラミング言語の習得

プロジェクト内で使用される言語に精通することは、きわめて重要です。これはあたりまえのことですが、実は簡単なことではありません。プログラムを記述できることは当然ですが、動作背景を理解できる能力を身に付ける必要性を強調しておきます。たとえば、開発者は時折、C++クラスであるローカル変数やオーバーロードC++演算子が、一時的にスタック上にデータを作成できることを忘れてしまいます。また、代入演算子は一見すると、単純なコードに見えるため、その背後で多量の実行コードを発生させていることなど気にもとめなくなります。Visual Basicなどもその背後で膨大な量のコードを生成しています。パフォーマンスバグをはじめとする多くのバグは、言語上の無理解に起因しています。このため、使用する言語特性を時間をかけて研究する努力が必要です。

■■ テクノロジとツールの理解

深刻なバグを取り除こうとする場合、使用する基盤テクノロジへの理解が不可欠になります。たとえば、COM(Component Object Model)がCOMオブジェクトをインスタンス化し、そのインターフェイスをどのように返してくるかを理解していれば、特定のインターフェイスが返されないときの解析に、それほど時間を必要としなくなるでしょう。これはISAPIフィルタなどについてもいえます。フィルタが正常に呼び出されているときに問題が発生した場合、INETINFO.EXEがどのようなタイミングで、どこにそのフィルタをロードしているのかを理解する必要があります。ソースコードや専門書籍をくまなく熟読する必要性を主張しているのではありません。使用するテクノロジの概略を理解する必要があることを述べているのです。そして、より重要なポイントは、必要なときに参照すべき情報が、どこにあるのかを整理しておく必要があることなのです。

テクノロジに加えて、使用するツールに精通することも重要です。本書の多くのページは、デバッガの高度な使用法に割かれていますが、Platform SDKなどには多数のツールが含まれていることを忘れるべきではありません。自分のシステム内で利用できそうなツール類を、時間のあるときに探してみるとよいでしょう。できれば、丸1日かけて、主要なツール類を検討してみるとよいでしょう。この1日の時間の投資は、決して無駄になりません。また、市販ツールの評価版をダウンロードし、実際に試してみるのも賢い方法です。将来、必ず役に立ちます。

■■ OSと環境の理解

OSに関する知識は、バグ解決にきわめて重要な意味を持ちます。このレベルでの知識が乏しい場合、バグの前でうろたえることになるでしょう。皆さんがネイティブコードを使用している場合、次のようなOS関連の質問にどのような回答を用意するでしょうか。

  • ダイナミックリンクライブラリ(DLL)とは何か。
  • イメージローダーはどのように動作しているか。
  • レジストリはどのような役割を持っているか。

また、マネージドコードを使用する場合には、次のような質問への回答を用意できる知識が必要です。

  • ASP.NETはページ内で参照されるコンポーネントをどのように検索しているか。
  • ファイナライザはいつ呼び出されているか。
  • アプリケーションドメインとアセンブリの違いは何か。

最も深刻なバグの多くは、プログラム内部でOS機能や環境を誤用したときに発生します。たとえば、不正なデータをOSに渡したり、あるいはOS機能を呼び出した後の影響に注意を払わない場合などに発生します。友人であるMatt Pietrek氏は、私のデバッギングの先生でもありますが、OSとCPUの知識があるかないかが、デバッグ専門家と並みの開発者の境界である、と述べています。

■■ CPUの理解

ネイティブコードをデバッギングする専門家になるためには、なんと言ってもCPUの知識が必要です。皆さんが遭遇する厄介なバグのほとんどを取り除くには、CPUに関する知識が不可欠になります。クラッシュしたときに、必ずソースコードが参照できるとは限りません。常にソースコード情報が利用できるのであれば、天国です。多くのクラッシュは、混合モードウィンドウの深海に私たちを突き落とします。私はかなり驚いていますが、ほとんどのエンジニアはアセンブリ言語を知りません。むしろ、アセンブリ言語に興味を示すエンジニアがほとんどいない、と言ったほうがよいでしょう。この言語を習得するのは、それほど難しいことではありません。アセンブリ言語を2、3時間ほど学習しておけば、デバッガ内で過ごす時間を大幅に削減できるでしょう。注意してほしいのは、アプリケーション全体をアセンブリ言語で開発する必要があると言っているわけではありません。そのようなことは私もできません。アセンブリコードを読むことができる知識を習得する必要性を、ここでは強調しているにすぎません。アセンブリ言語についての必要情報は第7章で提供しています。

1.2.2 | スキルセットの習得

テクノロジを扱う業種では、常に研究を怠ることなく、最新情報に精通している必要があります。知識を深めたり、新しい動きを把握するのは当然の努力です。私は、皆さんが参加するプロジェクトについては、何も具体的なことを知りませんから、支援の手を差し伸べることはできかねます。しかし、私が参考にしてきた情報を付録に紹介しておきます。皆さんが成長するうえでの参考になれば、さいわいです。

デバッギングに関する書籍や雑誌を読むことのほかに、ユーティリティを自分で書いてみるとよいでしょう。どのようなユーティリティでもかまいません。結局は実践することが最高の学習なのです。この業界にいる限り、コーディングとデバッギングを避けて通れません。コーディングやデバッギングスキルを高めるだけではなく、ユーティリティ作成を擬似的なプロジェクト(つまり、一定の時間内での高品質な製品開発)に見立て、プロジェクト計画やスケジューリングなどのソフトスキルを高めるとよいでしょう。

作業条件を決め、その条件を守りながらユーティリティを完成させる場合、「就職試験の面接時に提出できるほどの完成度を誇るか」という目標を立てるとよいでしょう。エンジニアの多くは、自分のユーティリティを面接会場に持参するようなことはしません。しかし、多くの会社では、実績のない人よりも実績のある人を採用するのが常です。少なくとも、自分のスキルを証明する材料にはなります。また、自分の空き時間を使ってそれなりの努力をしたのですから、その熱意は伝わるはずです。会社によっては、上位20人のリストに入れてくれるでしょう。

プログラミング言語、テクノロジ、OS、そしてCPUに関する知識を深めたい場合、他の人のソースコードを読むとよいでしょう。私はこれを実践した者として、相当の効果が期待できると断言します。ご存知のように、インターネット上には想像を絶するソースコードが公開されています。デバッガ内でさまざまなプログラムを動作させることにより、どのようなバグ回避策がとられているかを理解できます。また、自分でユーティリティを作成しているときに問題が発生した場合、インターネットから取得した同類のユーティリティに機能を追加し、その動作を観察してみるとよいでしょう。

さらに、テクノロジ、OS、および仮想マシン(CPU)に関する知識を深めたい場合、リバースエンジニアリングも有効です。アセンブリ言語に精通できるようになるだけではなく、デバッガの先進機能を理解するうえでの効果もあります。第6章と第7章を一読すれば、Microsoft中間言語(MSIL)とIA32アセンブリ言語の基礎知識を得られます。後は、必要に応じてリバースエンジニアリングを実際に行い、知識を深めてください。OSローダーを完璧にリバースエンジニアリングすることはお勧めできませんが、より小規模なシステム処理を解析してみるとよいでしょう。たとえば、CoInitializeExなどのネイティブコードの実装内容を、検討することをお勧めします。私はたいへん勉強になりました。マネージドコードの場合には、System.Diagnostics.TraceListenerクラスを検討してみるとよいでしょう。

書籍や月刊誌の購読、ユーティリティの開発、他のエンジニアのソースコードの検討、そしてリバースエンジニアリング。これは、すべてデバッギングスキルを高めるために有効です。しかし、なんと言っても最高の教材は、皆さんの友人であり同僚です。周囲のエンジニアに質問することを決してためらってはいけません。「なぜ、このコードが必要なのか」。「そのコードはどのように動作するのか」。どんどん質問するとよいでしょう。納期に苦しんでいない限り、彼らは喜んで回答してくれるでしょう。私はよく質問を受けますが、喜んで回答しています。むしろ、楽しんでいるといってもよいでしょう。実は、回答を探し出し、それをわかるように説明することも、スキルを上げるために必要なのです。プログラミングのニュースグループなども、質問するにはうってつけの場所です。私は、そのような場所に投稿される質問をよく読みます。すばらしい回答が公開されています。特に、MicrosoftがMVP(Most Valuable Professionals)と認定された人々からの回答には、目を見張るものがあります。

1.3 | デバッギングプロセス

ここでは、実践的なデバッギングプロセスを取り上げます。すべてのデバッギング作業に有効なプロセスを定義するのは、ちょっとした挑戦といえるでしょう。しかし、私自身と同僚などの経験談から導き出させるデバッギングアプローチは、奇妙な性格を持っています。つまり、そのアプローチは、私たちのような経験者には直感的に理解できるのですが、経験の少ない開発者が採用するには、その壁が高すぎるのです。

詳細については後述しますが、このデバッギングプロセスはロケット科学の学位を必要とはしません。しかし、デバッグ作業を開始するときには、必ず繰り返し実行する必要があります。私が推薦するデバッギングアプローチは、次のような9つのステップで構成されています。

ステップ1:バグを再現する。
ステップ2:バグ内容を書き留める
ステップ3:バグの原因は自分にあるとする。
ステップ4:細分化し、征服する。
ステップ5:創造的に考える。
ステップ6:ツールを活用する。
ステップ7:詳細デバッグを開始する。
ステップ8:バグの除去具合を検証する。
ステップ9:デバッグ内容を記録する。

問題とその発生場所がはっきりしているときには、いくつかのステップを割愛してもよいでしょう。しかし、ステップ1とステップ2は避けて通れません。ステップ3からステップ7までのいずれかのステップでソリューションを見い出し、バグを除去できるはずです。そのような場合、バグ修正後、ステップ8に飛び、修正コードをテストするようにしてください。図1-1にデバッギングプロセスを構成するステップを図示します。

図1-1 デバッギングプロセス

▲ 図1-1 デバッギングプロセス

1.3.1 | ステップ1:バグを再現する

このデバッギングプロセスで一番重要なのは、このステップ1、つまりバグを再現する作業です。バグの再現は、時には一筋縄ではいきません。時には、再現できないこともあります。再現できないということは、それを取り除くことができないことを意味します。バグを再現する作業を行うときには、極端な場面を想定する必要があるでしょう。私はかつて、プログラムを単に再実行しただけでは、再現できないバグに遭遇した経験があります。しかし、そのバグを引き起こす原因となったデータ条件を知っていましたから、私はデバッガからそのプログラムを起動し、バグを引き起こすデータを直接メモリに書き込みました。もちろん、うまくいきました。同期問題を扱っている場合、同じタスクを繰り返しロードするような処理が必要になるでしょう。バグが発生する状態を再現する必要があるからです。

ここまで読み進んできた皆さんは、「そうか、バグを再現することが重要なのか。バグが再現できれば、Johnのこの本は不要だ!」と考えはじめるかもしれません。問題は「再現性」というものです。再現できたとしても、バッグを修正できるとは限りません。私は、24時間に1回の割合で同一マシン内でバグを発生できるようになれば、そのバグの再現に成功したと考えています。これで十分なのです。なぜだかわかりますか? 答えは簡単です。1台のマシンでバグが再現できれば、それを30台のマシンに適応し、30回再現できるからです。多くの開発者は、多数のマシンでバグを再現する努力を怠っています。それは明らかに間違いです。30人の協力が得られれば、それはすばらしいことです。現実には、このようなことは不可能でしょうから、バグを発生させるユーザーインターフェイスを自動化するとよいでしょう。第17章で紹介するTesterアプリケーションや、市販の自動回帰テストなどが使えます。

1つの手順でバグを再現できれば、その他の方法で再現できるかどうかを確認する必要があります。バグによっては、1種類のコードパス内部で発生しますが、複数のコードパスを経由しながら最終的に発生するバグもあります。重要なことは、あらゆる角度から問題のバスに至るようすを理解することです。複数パスからバグを再現することにより、問題を引き起こしているデータと境界などのいくつかの条件を、よりいっそうはっきりさせることができます。また、既にご存知のように、バグによっては他のバグの発生原因を兼ねています。再現方法を多数用意できれば、それだけ解決時間が短縮できます。

バグを再現できない場合でも、自分のバグ追跡システムにそれを記録するようにしてください。私は、常にそのようにするとともに、再現できなかった旨のメモも挿入しています。このようにしておけば、他のエンジニアがそのコード部分を再利用しようとしたとき、彼ないし彼女は何か重要なことが過去に発生し、未解決であることを知ることができます。再現できないバグを記録するときには、可能な限りその背景などをわかりやすく文章化しておくとよいでしょう。きちんとした説明が残っているのなら、後日、必ず問題への解答が見つかるはずです。皆さんが見つけられなくとも、同僚が見つけてくれる可能性もあります。再現できないバグ情報が多量に記録されていれば、後日、それらを総合的に分析することにより、バグのパターンを見い出すようなこともできるでしょう。

1.3.2 | ステップ2:バグ内容を書き留める

皆さんが典型的なエンジニアリング専攻学生であるなら、おそらく数学やエンジニアリングの授業には熱心に参加していると思います。しかし、ライティングの単位は、やっとのことで取得している状態でしょう。実は、開発現場では、ライティングスキルはエンジニアリングスキル以上に重要なのです。遭遇するバグを記述し、それをユーザー、同僚、管理者、営業担当者などに口頭で説明する必要があるのです。深刻なバグが発生したときには、そのバグを再現した後でデバッグ作業を中断し、状況を詳しく記述しなければなりません。理想的には、バグ追跡システムに入力することになるでしょう。バグを取り除くのが皆さんの本業ですが、しかしそれをきちんと整理し、説明できることもとても大切なことなのです。また、他のエンジニアの説明文を読んで、以前には思いつかなかったような解決策が見つかることがよくあります。異なる視点からバグを観察できるようになったわけです。私は、数え切れないほどこのような経験をしています。

1.3.3 | ステップ3:バグの原因は自分にあるとする

私は長年、ソフトウェア開発に従事してきましたが、経験から言えば、コンパイラやOSに起因するエラーは数%にすぎないということです。このため、バグがあれば、私たちに原因がある確率が高くなります。つまり、バグの発生原因は自分にある、と考えることが重要です。また、それを前提として(ある意味で期待して)デバッグ作業を開始するとよいでしょう。バグが自分たちのコード内にあれば、少なくともそれを修正することができます。コンパイラやOS内部にあれば、問題はより深刻です。いずれにしても、まず自分のコード内のバグを取り除く努力をし、その後、より深刻なOS内部のエラーなどを検出するとよいでしょう。

1.3.4 | ステップ4:細分化し、征服する

バグを再現し、それをわかりやすく記録できれば、そのバグがどこで発生しているか、ある程度わかることになります。このため、バグを発生させている状況をより整理し、仮定を立てるとよいでしょう。しっかり仮定を立て、それを実証します。まずソースコードを読み、発生しているバグと実際のコードの関係を検討します。ソースコードを読みながら、発生している問題をじっくり考えます。クラッシュや問題発生時のマシン状態を確認しながら、そのコードが実行されるまでの過程を追います。数分経っても仮定が立証できない場合、そこで立ち止まり、状況を再度評価します。この時点では、それまで以上にバグ状況を理解していることになりますから、新たな仮定を立て再度実証する作業を繰り返します。

デバッギングは、バイナリ検索アルゴリズムのようなもの、と考えてしまうとよいでしょう。まず、バグの発生位置を見つけ出し、いろいろな仮定を立て、それを繰り返し実証します。運がよければ、バグとは無関係なプログラムコード部分を作業対象から除外できます。このように作業を進行していきながら、次々に無関係なコードセクションを除外し、疑わしいコードセクションのみを特定していきます。さらに仮定を立て、より詳細なバグ情報を収集しながら、バグ記録を更新します。私がこの作業を行うときには、3個から4個の仮定を立て、次のステップに進むことにしています。重要なことは、どの時点でデバッガを起動するか、ということです。つまり、本格的なデバッグ作業に入る前に、情報を絞りきることが重要です。この情報の絞りきりは、状態や変数の内容をチェックすることになりますから、ちょっとしたデバッグ作業となります。このステップでは、あくまでも情報の絞りきりがメインです。すべてを詳細にチェックする必要はありません。

1.3.5 | ステップ5:創造的に考える

取り除こうとしているバグが特定のマシンでのみ発生していたり、あるいは再現が難しいタイプの場合、視点を変えてそのバグを眺める必要があります。このステップでは、DLLのバージョンの不一致、OSの違い、プログラムコードやインストールに関する問題、その他の外部要因を考慮する必要があります。

時には2、3日、間を置いてみるのも効果があります。つまり、冷却期間を確保するわけです。これは意外に効果があり、1つのデバッグテクニックと考えてよいと思います。問題解決を無闇に繰り返していると、方向性を失い、大切な鍵さえ見落としてしまいます。位置を変え、遠くから眺め、潜在意識が働き出すのを待ってみるのです。皆さんは、家路についてからバグの原因が、わかったような経験もされていることでしょう。もちろん、納期が迫り、上司の顔色が厳しくなっているときには、髪をかきむしり続ける必要があるでしょう。しかし、焦りは禁物です。

かつて在籍した2、3の会社では、バグが発生すると作業を中断し、ミーティングをもっていました。そのようなミーティングは、「バグトーク」と呼ばれていたと思います。ご想像のように、バグトークは優先度の高いミーティングです。だれかの部屋に入り、ホワイトボードを使って問題を解説します。部屋に入り、マーカーのキャップをはずします。そして、ボードに問題の概要を描き始めます。描き始めたその瞬間に問題が解けたような経験が何度もあります。会議が始まる前に、問題が解けてしまったのです。ここで私が指摘したいのは、問題を理解し、それをだれかに説明することの重要性なのです。個々の木に注意を払っていたのでは、森全体を見ることはできません。バグトークの相手を選択する際には、可能な限り、プロジェクトとは関係のないエンジニアを選ぶとよいでしょう。まったくの他人にするのです。プロジェクト内の同僚と異なり、まったくの他人は同じ前提を持っていません。そのような人に問題を説明するためには、まず自分の頭を冷やし、問題を0から分析する努力が必要になります。

バグトークの相手は人間であるとは限りません。私の場合、たいへんおもしろいことに、飼っている猫たちであることがあります。私の猫たちは、優れたデバッガなのです。実際、私は彼らとやりとりすることにより、いろいろな難解なバグを解決しました。猫たちとのトークが終わると、私は問題を自分の部屋のホワイトボードに描画します。描画しながら、なんとも不思議なことに、問題への解答が自然とどこからともなくやってくるのです。

バグトークの相手には、伴侶や親しい人を選んではなりません。親しい関係は、それだけで問題解決の障害なのです。理由はいろいろですが、ここでははっきり申し上げません。

1.3.6 | ステップ6:ツールを活用する

不思議な方針を採用している会社があります。エラー検出、パフォーマンスチェック用のデバッグツールは、数千ドルで購入できます。この数千ドルの出費により、エンジニアは数分でバグを検出できるようになります。しかし、会社によっては、この数千ドルの投資を嫌がり、社内エンジニアは数週間のデバッグ時間を費やすことになります。人件費は数千ドルでは済まないはずです。

CompuwareやRationalなどのいくつかの会社は、マネージドとネイティブコード用の優れたツールを出荷しています。私は本格的なデバッグ作業に入る前に、必ずそれらのツールから自分のコードを実行しています。ネイティブコード内のバグ検出は、マネージドコード内のバグ検出以上の難しさがあります。これは、ツールの選択が重要な意味を持つことを示しています。Compuware NuMegaからは、BoundsChecker(エラー検出ツール)、TrueTime(パフォーマンスツール)、TrueCoverage(コードカバーレッジツール)が出荷されています。Rationalからは、Purify(エラー検出)、Quantify(パフォーマンス測定)、PureCoverage(コードカバーレッジ)が販売されています。ここで言いたいことは、サードパーティ製ツールを使用していない場合、一般的には必要以上の時間をかけている、ということです。より端的に表現すれば、ここで紹介したツールを使用すると、デバッグ作業を効率的に行えるため、時間も経費も必要以上にかからない、ということです。

この種のタイプのツールを、それほど使用した経験のない人がいるでしょうから、少しだけ説明しておきます。エラー検出ツールは、不正なメモリアクセス、システムAPIやCOMインターフェイスへの不正パラメータ渡し、メモリリーク、およびリソースリークなどを検出する機能を提供しています。パフォーマンスツールは、アプリケーションの動作を追跡し、動作低下の原因となるボトルネックを検出する機能を提供しています。通常、皆さんが想定していないようなボトルネックを特定してくれます。一方、コードカバーレッジツールは、プログラム実行時に、実行されることのないソースコード行を表示する機能を提供してくれます。バグを見つけている場合、必ず実行される行を調べたいでしょうから、このツールの情報は時間短縮に貢献します。

1.3.7 | ステップ7:詳細デバッグを開始する

私は、詳細デバッグとステップ4で触れた単純なデバッグ作業を区別しています。この区別は、デバッガ内で行う作業レベルを示します。単純なデバッグ作業では、2、3種類の状態と変数を調べるだけです。これとは対照的に、詳細デバッグでは、プログラム動作をかなりの時間をかけて解析します。デバッガの高度な機能を使用するのは、まさにこの詳細デバッグ時です。つまり、デバッガをこき使い、各種の詳細情報を報告させます。デバッガの高度な機能については、第6章から第8章で詳しく解説します。

単純なデバッグ作業を行うときと同じように、詳細なデバッグ作業を行うときにも、バグがどこで発生しているかをある程度予測し、その上でデバッガを起動することが重要です。つまり、しっかりとした仮定を立て、それを実証するためにデバッガを利用するという姿勢が必要です。無闇にデバッガを起動し、あちこち見て回っても意味はありません。デバッガを起動する前に、仮定している内容を文書化しておくとよいでしょう。文書化すると、やるべき内容をきちんと頭の中で整理できます。

また、詳細デバッグでは、バグ修正用コードをデバッガ内で定期的にレビューすることも重要です。私は2台のマシンを用意し、修正コードをレビューすることにしています。1台のマシンでコードを修正し、残りのもう1台のマシンでその修正済みコードを実行するのです。この二重、三重のチェックは、コードベースが不安定になるのを避ける意味でもたいへん重要です。上司というのは、修正コードを投入し、特定の動作を確認しただけでは不安を抱える人種なのです。標準的な環境でも動作確認を行っておきましょう。

プロジェクト設計を正しく行い、本章で紹介したデバッグステップをきちんと守り、その上で、第2章で取り上げるアドバイスを参考にすれば、詳細デバッグに費やす時間を大幅に短縮できるはずです。

1.3.8 | ステップ8:バグの除去具合を検証する

バグを完全に修正できた、と判断した後は、その新規コードを繰り返しテストします。バグフィックスもテストする必要があるのです。バグが独立したモジュール内で発生し、しかも一度しか実行されない場合、バグフィックスのテストは簡単に終了できます。しかし、データ構造体などを処理するコアモジュール内のコードを修正した場合、その修正コードが新たなバグを生み出す可能性があります。あるいは、プロジェクト内の他の部分に影響を与えることも十分にあり得ます。

重要なコードのバグフィックスをテストするときには、すべてのデータ条件下で動作することを確認する必要があります。つまり、誤った不正なデータを入力したときに、動作不能に陥るようではバグフィックスとはいえません。1つのバグを修正したために、2つの新たなバグを発生させるようなデバッグは最悪です。重要なモジュールに変更を加える場合、すべてのチームメンバーにその変更内容を通知する必要があります。このようにすれば、すべてのメンバーがその変更の二次的な影響を監視できるようになります。

デバッグ物語

これは統合といえるか?

■発生したバグ

あるとき、NuMegaで一緒に作業していた開発者の1人が、NuMegaのVisual C++統合開発環境(VC IDE)に重大なバグがある、と主張しました。彼のマシンでは正常に動作しなかったのです。NuMegaのVC IDEを知らない人もいるでしょうから、ここで概要だけ説明しておきます。NuMegaのソフトウェア製品はここ数年、VC IDEに統合できるように設計されています。つまり、NuMegaのウィンドウ、ツールバー、メニューなどは、VC IDE内に組み込まれ、表示されることになっています。

■デバッグ作業

彼はカーネルデバッガであるSoftICEを起動し、数時間、バグとその背景を調べていました。しばらくしてから、彼はOS内に多数のブレークポイントを設定する行動に出ました。そして、ついに彼は、「バグ」を見つけ出しました。VC IDEを起動したとき、CreateProcess関数が呼ばれ、\\R2D2\VSCommon\MSDev98\Bin\MSDEV.EXEへのアクセスが発生していることに気付きました。彼が予想していた、C:\VSCommon\MSDev98\Bin\MSDEV.EXEへのアクセスではなかったのです。表現を変えれば、ローカルマシン(C:\VSCommon\MSDev98\Bin\MSDEV.EXE)からVC IDEを起動するのではなく、彼の古いマシン(\\R2D2\VSCommon\MSDev98\Bin\MSDEV.EXE)からそれを起動していたのです。なぜ、このようなことが発生したのでしょう。

その開発者は新規マシンを手に入れ、その直後にNuMegaのVC IDEをフルインストールしていました。効率的にセットアップするために、彼はVC IDEのインストールされていない、古いマシンのデスクトップリンク(LNKファイル)を新しいマシンにコピーしました。つまり、マウスでドラッグしていたわけです。LNKファイルをドラッグすると、その内部リンク情報が更新され、元のリンクの位置を保持します。このため、彼は古いマシンへのアクセス情報を保持するデスクトップアイコンリンク経由で、VC IDEを起動していたことになります。つまり、新しいマシンからIDEを起動していたのではないのです。彼は理解していませんでしたが、依然として彼は古いマシンからIDEを起動していたのです。

■教訓

この開発者は初歩的な過ちを犯しています。いきなりカーネルデバッガで詳細デバッグ作業を開始しています。彼はその前に、いろいろな方法でバグを再現する作業をすべきだったのです。デバッギングプロセスの「ステップ1:バグを再現する」で、私はいろいろな方法でバグを再現することを主張しました。この作業は、デバッグ対象となる問題を絞りきる作業です。複数のバグが複雑に絡み合っている場合もあります。このような場合、第1原因を特定しなければなりません。彼が「ステップ5:創造的に考える」を踏んでいたなら、より良い結果が出ていたでしょう。デバッガ環境にいきなり飛び込むのではなく、その前に、問題をいろいろな角度から考えることができたからです。

1.3.9 | ステップ9:デバッグ内容を記録する

バグ(あるいはそれと思わしきものも含む)を修正したときには、必ず時間を取り、知り得た背景をすばやく記述しておく必要があります。私は、「これぞ」と思えるバグはジャーナルに記録することが好きです。記録しておけば、後になってその問題を見つけ出し、そして、どのようにして、その解決策を用意したかを振り返ることができます。より重要なことは、当時の試行錯誤から多くのことを学べることです。失敗は多くの教訓を与えてくれますから、デバッグスキルが確実に向上します。つまり、過去の記録を振り返ることにより、私は1歩でも2歩でもデバッグ専門家に近づけるのです。デバッグ作業を経験すれば、ソフトウェア開発全般にわたる知識が深まります。すべての経験を活かしきりましょう。そのためには、記録を取っておくことが大切です。

バグ修正後に行うべき重要な作業は、情報の整理であり、共有です。デバッグ作業から知り得た情報を同僚と共有することです。特に、そのバグがプロジェクトに固有のものであれば、知り得た情報は貴重ですから、同僚と共有するようにしましょう。同僚が同じようなバグに遭遇した場合、皆さんが収集し、記録しておいた情報はその同僚を救うことになるでしょう。

1.3.10 | デバッギング秘儀

最後に、デバッギングに関する秘儀を紹介しておきましょう。デバッガは皆さんからのすべての質問に回答してくれます。もちろん、質問内容が正確でなければなりません。また、仮定を立てておく必要もあるでしょう。しっかりとした仮定を立て、そこから質問内容を考え出すのです。デバッガは、その質問への回答を探し出す手助けをしてくれるはずです。ステップ7で触れたように、デバッガを起動する前に、打ち立てた仮定を文章化するとよいでしょう。デバッガを使用するときには、その目的を明確にしておきます。

デバッガは道具にすぎません。本質的には、ねじ回しと同じなのです。指示されたことしかできません。本当のデバッガは私たちの頭脳です。この点を決して忘れないようにしましょう。

まとめ

本章では、まずバグを定義し、バグの原因となる開発プロセスの問題を説明しました。その後、実際のデバッグ作業を開始する前に、知っておくべき事項を紹介しました。最後に、プログラムコードをデバッグするときに参考となるデバッグプロセスを示しました。

最良のデバッグ方法は、バグを発生させないような開発姿勢を身に付けることです。適切なプロジェクト計画を立て、品質にこだわり、そして、製品開発に必要なテクノロジ、OS、CPUなどの知識を深めておけば、デバッギングに要する時間を最小に抑えることができます。