ソフトウェア開発の「パンドラの箱」を開けないために山浦恒央の“くみこみ”な話(18)

最終テストでバグを発見! 納期が迫っているが……。修正するべきか? それとも放置か? 「未修正のバグが残っていない」について紹介

» 2010年04月16日 00時00分 公開
[山浦恒央 東海大学 大学院 組込み技術研究科 准教授(工学博士),@IT MONOist]

 前回のコラム「テストでの『ダメな猫』『普通の猫』『優秀な猫』」では、7つの「基本的な出荷基準」を挙げ、「6.バグの発生が頭打ちになった」について解説しました。今回は、最後の基準である「7.未修正のバグが残っていない」を紹介します。


 まずは、7つの基本的な出荷基準について再掲します。

  1. 全機能をテストした 
    ブラックボックス/ホワイトボックス・テストで同値分割を実施した。
  2. 境界条件をテストした 
    ブラックボックス/ホワイトボックス・テストの境界値分析を実施した。
  3. 未実行コードがない 
    ホワイトボックス・テストのC0パス網羅を満足した。
  4. エラー・ゲシング(Error Guessing)を実施した 
    バグを想定し、それを摘出するためのテスト項目を設計・実施した。
  5. 長時間耐久テスト、過負荷テストを実施した 
    48時間の連続運転や、過負荷状態での稼働テストを実施した。
  6. バグの発生が頭打ちになった 
    いわゆる「バグ摘出曲線」がフラットになった。
  7. 未修正のバグが残っていない 
    摘出したバグは全件修正した。

 それでは、7つの基本的な出荷基準の最後、「7.未修正のバグが残っていない」について見ていきましょう。

1.バグの検出から修正までの7ステップ

 「バグを見つけて修正する」と書くと、ものすごく簡単に聞こえます。「そんなの当たり前だろ!」という声も聞こえてきそうです。学生の課題プログラミングのように、100LOC(Lines Of Code:ソース・コードの行数を表す専門用語で、エル・オー・シーと読む)程度の小さいソフトウェアなら、「バグを見つけて修正する」ことは非常に簡単です。一方、最近の携帯電話のように、10MLOC(Mega Lines Of Code:100万行のこと。ちなみに、1000行はKLOC:Kilo Lines Of Code)を超える“超巨大”で、かつ、プロセスの追い越しが発生するリアルタイムOS系ベースの“超複雑”なソフトウェアでは、「バグを見つける」と「修正する」の間に、非常に大きなギャップがあります。

 一般的に、バグを検出してから修正するまでの段階は、以下のようになります。

  1. ヘンな動作をすることがある(気のせいかもしれない……)
  2. 明らかにヘンな動作をする(間違いなく、ヘンだ……)
  3. ヘンな動作を再現させられる(最初にして最大の関門)
  4. 修正方法が判明した(第2関門)
  5. 修正し、周辺をテストした(20%の確率で“新しいバグ”を作り込む)
  6. 類似バグをチェックし、必要な個所を修正した(第3関門、非常に重要)
  7. 回帰テストを実施した

1.1.再現条件の洗い出し

 「ヘンな動作を検出する」ことは、それほど難しくはありません。テスト項目を実行して、期待した出力が返ってこなかったり、システムがフリーズして動かなくなったりするなど、簡単に「不正動作」は見つかります。

 最初にして最大の難問は、「不正動作が発生する条件が分かる」ということです。バグを検出したプログラマが、全力で取り組むのが、「再現条件(reproducing conditions)」の特定で、再現条件が分かれば、バグは解決できたも同然です。

 プロセスが毎回、順序どおりに整然と進むバッチ系のソフトウェアの場合、再現条件を見つけるのはかなり簡単でしょう。恐らく、バッチ系のバグを検出した瞬間、「ここの処理が間違っているんだろうなぁ」と、再現条件を飛び越えて、バグが存在する場所まで頭に浮かびます。

 しかし、リアルタイムOS系の場合はそう簡単にはいきません。例えば、携帯電話でよくあることですが、音声通話の最中にメールを受信した瞬間、すべてのプロセスがフリーズしたとします。この不正動作は、プロセスのタイミングや組み合わせが関係するため、バグの再現条件はそう簡単には分かりません。エンジニアが3人集まって、プログラムを1ステップずつ解析していっても、きっとバグの原因は分からないでしょう。結局、10人が1日18時間もかけて同じような操作をし、再現させようとするのですが、2週間たっても再現せず、出荷時期が迫ってくる……。プロジェクト・マネージャの胃に1ダースの穴が開くのはこんなときです。この場合、どう対策すればよいのかは後述しますが、携帯電話のような複雑怪奇なプロセスが絡むソフトウェアの最終テストでは、こんな難問が必ず5、6個は発生します。

 どんなバグにせよ、最終テストで出たバグの再現条件が分かると、プロジェクト・マネージャだけでなく、QAエンジニアやプロジェクトの全員がホッとするのです。

1.2.バグの修正

 バグの再現条件が分かれば、バグは修正できたも同然です。重要なことは、修正した後、“どんなチェックをするか”にあります。

(1)回帰テストによる全機能検査(不可解な現象を起こさないために) 
 修正後、変更した個所の周辺をテストするのは当たり前です。周辺のテストをしないプログラマはいません。エンドゲームでは、たとえ1行だけの変更であっても、必ず、全機能のテストを実施する必要があります。いわゆる、「回帰テスト(Regression Test)」です。1行だけの変更であっても、それがどこでどんな影響を及ぼすか、誰にも分かりません。まったく無関係に見える機能が動かなくなる可能性もあります。この不可解・不思議な現象は、例えば、トイレの水漏れを修理したらキッチンの右側のガスコンロが使えなくなったという雰囲気でしょうか? こんな不思議な現象(専門用語で、機能退行:Functional Degradationと呼びます)を摘出するため、変更が終了するたびに、必ず全機能のサンプルを実行させる必要があります。

 重要なことは、ある個所の変更をチェックするために設計したテスト項目を回帰テストの項目に追加することです。

(2)類似バグのチェック(隠れたゴキブリを探す) 
 このコラムで、何度も繰り返して述べていることですが、摘出したバグを修正するだけでは、ソフトウェアの品質は確保できません。必ず、類似不良をチェックしなければなりません。ゴキブリが1匹見つかったら、隠れたところにその10倍は潜んでいることでしょう。文字通り、「隠れた虫」をたたき出せるかどうかが、高品質プログラムを作れるかどうかの分かれ道になります。

2.プログラマと警察官

 ソフトウェア開発での大原則は、もちろん「摘出したバグはすべて修正する」です。ソフトウェア技術者として(特に、日本では)、摘出したバグを修正しないで出荷することは、暴力事件の現場を通り掛かった警察官が、それを無視して立ち去るのと同じぐらい職業倫理に反することです。しかし、ソフトウェアの開発では、摘出したバグを修正しない方がよい場合も少なくありません。特に、出荷日が目前に迫ったタイミングでは、そのままバグを放置した方が望ましいこともあるのです。

 修正するか、放置するか、この「距離感」は非常に難しいのですが、以下を参考に、理解してもらえればと思います。

3.バグを修正できないさまざまなケース

 出荷時期が間近に迫った最終段階で、摘出したバグを修正できない、あるいは、修正しないことがあります。それは一体どんなケースなのでしょうか? 以下で、それぞれについて説明します。

3.1.再現条件が分からない

 携帯電話で音声通話中に添付ファイル付きのメールを受信し、その最中に、割り込みで音声通話が飛び込んできて、キャッチフォン機能が作動しようとした瞬間に、第3の音声通話が入り、携帯電話が動かなくなった……など。サイコロを100個投げて、全部6の目が出るぐらいの確率でしか起きないバグもあります。こうなると、再現条件は簡単には分かりません。かといって、重要機能なので、対策しないわけにはいきません。

 2週間後には、何が何でも出荷しなければならないので、再現条件を見つけるため、人海戦術として、20人で再現テストをしたところ、同じ不良が出ない……なんてことがよくあります。「バグじゃなく、単なる勘違いだったかも……」なんて考えが脳裏をよぎりますが、これまでの経験上、携帯電話のようにユーザーが数万人もいると、あれほどテストで出なかったバグも、1カ月ほどで必ずユーザーの1人が「動かないんですが!」といってくるはずです。安価で小さくて数を売る組み込み系製品では、不良はほとんど市場で再現します。

 このような場合、どうしたらいいのか? マーケットで再現することを十分認識し、出荷するのですが、出荷後に再現する場合に備えて、バグのデータを集められるよう製品に「データ収集ロジック」や「罠(わな)パッチ」を埋め込んでおくことが必要です。

3.2.発生頻度が非常に低いバグ

 出荷予定は来週。プログラムは何の問題もなく順調に動作しています。気持ちに余裕が出て、軽い気持ちでちょっと変わったオペレーションをしたところ、一見、エラーに見えるけれども正常なパラメータが、異常データとして弾かれてしまった……。見なかったことにしたいけれど、エンジニアとしての良心が許しません。そこで、再現条件をチェックしたところ、再現する確率は100年に1回以下であることが分かりました。さて、どうすればいいんだろうかと悩む……。こんなケースは、意外に少なくありません。

 発生頻度が非常に低く、重要度も極めて低いバグは、恐らく修正しないでそのまま出荷することになると思います。この理由は、後述の「4.パンドラの箱」を参照してください。

3.3.重要度が非常に低いのに、変更作業が非常に大きい

 例えば、ディスクに出力しようとしたら出力障害が発生して、その障害をコンソールに報告しようとしたら、コンソールにハードウェア・エラーが発生して、それをロギング・ファイルに出力しようとしたら、また出力障害が発生して……。このように、エラー処理のエラー処理のエラー処理は、発生頻度が低く、重要度も高くありません。すべての出力障害エラー処理に同じバグがあるとすると、修正個所は広範囲に及ぶことが予想されます。

 さて、どうするか? 出荷まで1カ月以上の余裕がある場合は、まとめて修正するでしょうが、来週出荷しなければならないのなら、わたしは手を付けません。正確には、「怖くて、手を付けられない」です。後述しますが、開発の最終段階で、広範囲に及ぶ大修正を実施するのは、非常に危険です。修正すると、いま正常に動作している機能が急に動かなくなる可能性があります。修正した個所とまったく別の機能が、なぜか、動かなくなることは誰しも経験することです(詳細は、「4.パンドラの箱」で)。

 とにかく、エンドゲームでのバグ修正は、安易に行ってはなりません。

3.4.ある機能にバグが存在するが、別の代替機能がある

 あるプログラムで、ディスク上のデータを「削除」したり、ディスクAからディスクBへ「コピー」したりする機能は正常に動作するのですが、「移動」させる機能にバグがあるとします。移動は、コピーと削除の組み合わせで実現できるので、移動機能にバグがあって動作しなくても、代替機能があることになります。

 移動のようなメイン機能のバグは、代替機能があるとはいえ、動作しないとまずいので、修正するでしょうが、ほとんど使わない機能で、さらに、特殊な条件の場合に異常動作する場合、代替機能があれば修正しない場合があり得ます。これは、次に紹介する「4.パンドラの箱」現象を恐れるためです。

4.パンドラの箱

 開発の最終段階、いわゆる、エンドゲームでは不必要な作業をしてはなりません。いや、している時間はないはずなのですが、「エンジニアの良心」に律儀に従うと、つい「出来心」でやってしまうことがあります。

 例えば、初期設定モジュールで、以下のようなソース・コードがあるとします。

        :
OldStudentID=NullID;     //旧学生番号をクリアする
NewStudentID=NullID;     //新学生番号をクリアする
NewStudentID=NullID;     //新学生番号をクリアする
InUserFlag01=false;      //「登録済みユーザー」フラグをOFFにする
OutUserFlag01=false;     //「削除予定ユーザー」フラグをOFFにする
        : 

 2行目と3行目は、明らかに同じステートメントです。美意識が高くまじめで完ぺき主義のエンジニアは、こんなソース・コードを見ると我慢できません。何かのついでに、重複している行を削除してしまいます。しかし、そんなことをすると、まったく関係のない機能が動作しなくなる可能性があるのです。

 当然、どこかにバグは存在します。自分が作ったプログラムのバグではなく、コンパイラの不良、あるいはOSのバグの可能性もあり得ます。1行削除したため生成されるオブジェクト・コードの大きさが変わって、いままで隠れていた不良が表面化することもあるでしょう。どこかにバグがあるのですが、来週出荷しなければならないという大混乱期は、出荷することが最大の目的です。悠長に、どこにバグがあるのかを丹念に検討している時間はありません。

 ベテランのソフトウェア技術者は、正常に稼働しているプログラムに対して、上記の「重複行の削除」のようなことはしません。それは、「機能退行」を恐れるためです。機能退行という名のパンドラの箱は、絶対に開けてはなりません。パンドラの場合、最後に「希望」が残っていましたが、ソフトウェア開発の場合、「分析困難なバグ」が残るだけです……。

5.バックログの恐怖

 修正している時間がないため、未修正のままバグや時間切れのために実装できなかった機能を「バックログ」と呼びます(ある意味、「借金」です)。10年前のアメリカの統計情報によると、PC用のプログラムで19カ月分、マイクロ・コンピュータ系で26カ月分、メインフレーム系ソフトウェアで46カ月分のバックログがあるそうです。積み残しの機能も含めた値なので、バグだけとはいい切れませんが、それにしても、物すごい量です。恐ろしいことに、年月が経過するに従って、量が増えているそうです。これは、ソフトウェア技術者にとっての“恐怖”でしょう。(次回に続く)

【 筆者紹介 】
山浦 恒央(やまうら つねお)
東海大学 大学院 組込み技術研究科 助教授(工学博士)

1977年、日立ソフトウェアエンジニアリングに入社、2006年より、東海大学情報理工学部ソフトウェア開発工学科助教授、2007年より、同大学大学院組込み技術研究科助教授、現在に至る。

主な著書・訳書は、「Advances in Computers」 (Academic Press社、共著)、「ピープルウエア 第2版」「ソフトウェアテスト技法」「実践的プログラムテスト入門」「デスマーチ 第2版」「ソフトウエア開発プロフェッショナル」(以上、日経BP社、共訳)、「ソフトウエア開発 55の真実と10のウソ」「初めて学ぶソフトウエアメトリクス」(以上、日経BP社、翻訳)。


Copyright © ITmedia, Inc. All Rights Reserved.