最終テストでバグを発見! 納期が迫っているが……。修正するべきか? それとも放置か? 「未修正のバグが残っていない」について紹介
前回のコラム「テストでの『ダメな猫』『普通の猫』『優秀な猫』」では、7つの「基本的な出荷基準」を挙げ、「6.バグの発生が頭打ちになった」について解説しました。今回は、最後の基準である「7.未修正のバグが残っていない」を紹介します。
まずは、7つの基本的な出荷基準について再掲します。
それでは、7つの基本的な出荷基準の最後、「7.未修正のバグが残っていない」について見ていきましょう。
「バグを見つけて修正する」と書くと、ものすごく簡単に聞こえます。「そんなの当たり前だろ!」という声も聞こえてきそうです。学生の課題プログラミングのように、100LOC(Lines Of Code:ソース・コードの行数を表す専門用語で、エル・オー・シーと読む)程度の小さいソフトウェアなら、「バグを見つけて修正する」ことは非常に簡単です。一方、最近の携帯電話のように、10MLOC(Mega Lines Of Code:100万行のこと。ちなみに、1000行はKLOC:Kilo Lines Of Code)を超える“超巨大”で、かつ、プロセスの追い越しが発生するリアルタイムOS系ベースの“超複雑”なソフトウェアでは、「バグを見つける」と「修正する」の間に、非常に大きなギャップがあります。
一般的に、バグを検出してから修正するまでの段階は、以下のようになります。
「ヘンな動作を検出する」ことは、それほど難しくはありません。テスト項目を実行して、期待した出力が返ってこなかったり、システムがフリーズして動かなくなったりするなど、簡単に「不正動作」は見つかります。
最初にして最大の難問は、「不正動作が発生する条件が分かる」ということです。バグを検出したプログラマが、全力で取り組むのが、「再現条件(reproducing conditions)」の特定で、再現条件が分かれば、バグは解決できたも同然です。
プロセスが毎回、順序どおりに整然と進むバッチ系のソフトウェアの場合、再現条件を見つけるのはかなり簡単でしょう。恐らく、バッチ系のバグを検出した瞬間、「ここの処理が間違っているんだろうなぁ」と、再現条件を飛び越えて、バグが存在する場所まで頭に浮かびます。
しかし、リアルタイムOS系の場合はそう簡単にはいきません。例えば、携帯電話でよくあることですが、音声通話の最中にメールを受信した瞬間、すべてのプロセスがフリーズしたとします。この不正動作は、プロセスのタイミングや組み合わせが関係するため、バグの再現条件はそう簡単には分かりません。エンジニアが3人集まって、プログラムを1ステップずつ解析していっても、きっとバグの原因は分からないでしょう。結局、10人が1日18時間もかけて同じような操作をし、再現させようとするのですが、2週間たっても再現せず、出荷時期が迫ってくる……。プロジェクト・マネージャの胃に1ダースの穴が開くのはこんなときです。この場合、どう対策すればよいのかは後述しますが、携帯電話のような複雑怪奇なプロセスが絡むソフトウェアの最終テストでは、こんな難問が必ず5、6個は発生します。
どんなバグにせよ、最終テストで出たバグの再現条件が分かると、プロジェクト・マネージャだけでなく、QAエンジニアやプロジェクトの全員がホッとするのです。
バグの再現条件が分かれば、バグは修正できたも同然です。重要なことは、修正した後、“どんなチェックをするか”にあります。
(1)回帰テストによる全機能検査(不可解な現象を起こさないために)
修正後、変更した個所の周辺をテストするのは当たり前です。周辺のテストをしないプログラマはいません。エンドゲームでは、たとえ1行だけの変更であっても、必ず、全機能のテストを実施する必要があります。いわゆる、「回帰テスト(Regression Test)」です。1行だけの変更であっても、それがどこでどんな影響を及ぼすか、誰にも分かりません。まったく無関係に見える機能が動かなくなる可能性もあります。この不可解・不思議な現象は、例えば、トイレの水漏れを修理したらキッチンの右側のガスコンロが使えなくなったという雰囲気でしょうか? こんな不思議な現象(専門用語で、機能退行:Functional Degradationと呼びます)を摘出するため、変更が終了するたびに、必ず全機能のサンプルを実行させる必要があります。
重要なことは、ある個所の変更をチェックするために設計したテスト項目を回帰テストの項目に追加することです。
(2)類似バグのチェック(隠れたゴキブリを探す)
このコラムで、何度も繰り返して述べていることですが、摘出したバグを修正するだけでは、ソフトウェアの品質は確保できません。必ず、類似不良をチェックしなければなりません。ゴキブリが1匹見つかったら、隠れたところにその10倍は潜んでいることでしょう。文字通り、「隠れた虫」をたたき出せるかどうかが、高品質プログラムを作れるかどうかの分かれ道になります。
ソフトウェア開発での大原則は、もちろん「摘出したバグはすべて修正する」です。ソフトウェア技術者として(特に、日本では)、摘出したバグを修正しないで出荷することは、暴力事件の現場を通り掛かった警察官が、それを無視して立ち去るのと同じぐらい職業倫理に反することです。しかし、ソフトウェアの開発では、摘出したバグを修正しない方がよい場合も少なくありません。特に、出荷日が目前に迫ったタイミングでは、そのままバグを放置した方が望ましいこともあるのです。
修正するか、放置するか、この「距離感」は非常に難しいのですが、以下を参考に、理解してもらえればと思います。
出荷時期が間近に迫った最終段階で、摘出したバグを修正できない、あるいは、修正しないことがあります。それは一体どんなケースなのでしょうか? 以下で、それぞれについて説明します。
携帯電話で音声通話中に添付ファイル付きのメールを受信し、その最中に、割り込みで音声通話が飛び込んできて、キャッチフォン機能が作動しようとした瞬間に、第3の音声通話が入り、携帯電話が動かなくなった……など。サイコロを100個投げて、全部6の目が出るぐらいの確率でしか起きないバグもあります。こうなると、再現条件は簡単には分かりません。かといって、重要機能なので、対策しないわけにはいきません。
2週間後には、何が何でも出荷しなければならないので、再現条件を見つけるため、人海戦術として、20人で再現テストをしたところ、同じ不良が出ない……なんてことがよくあります。「バグじゃなく、単なる勘違いだったかも……」なんて考えが脳裏をよぎりますが、これまでの経験上、携帯電話のようにユーザーが数万人もいると、あれほどテストで出なかったバグも、1カ月ほどで必ずユーザーの1人が「動かないんですが!」といってくるはずです。安価で小さくて数を売る組み込み系製品では、不良はほとんど市場で再現します。
このような場合、どうしたらいいのか? マーケットで再現することを十分認識し、出荷するのですが、出荷後に再現する場合に備えて、バグのデータを集められるよう製品に「データ収集ロジック」や「罠(わな)パッチ」を埋め込んでおくことが必要です。
出荷予定は来週。プログラムは何の問題もなく順調に動作しています。気持ちに余裕が出て、軽い気持ちでちょっと変わったオペレーションをしたところ、一見、エラーに見えるけれども正常なパラメータが、異常データとして弾かれてしまった……。見なかったことにしたいけれど、エンジニアとしての良心が許しません。そこで、再現条件をチェックしたところ、再現する確率は100年に1回以下であることが分かりました。さて、どうすればいいんだろうかと悩む……。こんなケースは、意外に少なくありません。
発生頻度が非常に低く、重要度も極めて低いバグは、恐らく修正しないでそのまま出荷することになると思います。この理由は、後述の「4.パンドラの箱」を参照してください。
例えば、ディスクに出力しようとしたら出力障害が発生して、その障害をコンソールに報告しようとしたら、コンソールにハードウェア・エラーが発生して、それをロギング・ファイルに出力しようとしたら、また出力障害が発生して……。このように、エラー処理のエラー処理のエラー処理は、発生頻度が低く、重要度も高くありません。すべての出力障害エラー処理に同じバグがあるとすると、修正個所は広範囲に及ぶことが予想されます。
さて、どうするか? 出荷まで1カ月以上の余裕がある場合は、まとめて修正するでしょうが、来週出荷しなければならないのなら、わたしは手を付けません。正確には、「怖くて、手を付けられない」です。後述しますが、開発の最終段階で、広範囲に及ぶ大修正を実施するのは、非常に危険です。修正すると、いま正常に動作している機能が急に動かなくなる可能性があります。修正した個所とまったく別の機能が、なぜか、動かなくなることは誰しも経験することです(詳細は、「4.パンドラの箱」で)。
とにかく、エンドゲームでのバグ修正は、安易に行ってはなりません。
あるプログラムで、ディスク上のデータを「削除」したり、ディスクAからディスクBへ「コピー」したりする機能は正常に動作するのですが、「移動」させる機能にバグがあるとします。移動は、コピーと削除の組み合わせで実現できるので、移動機能にバグがあって動作しなくても、代替機能があることになります。
移動のようなメイン機能のバグは、代替機能があるとはいえ、動作しないとまずいので、修正するでしょうが、ほとんど使わない機能で、さらに、特殊な条件の場合に異常動作する場合、代替機能があれば修正しない場合があり得ます。これは、次に紹介する「4.パンドラの箱」現象を恐れるためです。
開発の最終段階、いわゆる、エンドゲームでは不必要な作業をしてはなりません。いや、している時間はないはずなのですが、「エンジニアの良心」に律儀に従うと、つい「出来心」でやってしまうことがあります。
例えば、初期設定モジュールで、以下のようなソース・コードがあるとします。
: OldStudentID=NullID; //旧学生番号をクリアする NewStudentID=NullID; //新学生番号をクリアする NewStudentID=NullID; //新学生番号をクリアする InUserFlag01=false; //「登録済みユーザー」フラグをOFFにする OutUserFlag01=false; //「削除予定ユーザー」フラグをOFFにする :
2行目と3行目は、明らかに同じステートメントです。美意識が高くまじめで完ぺき主義のエンジニアは、こんなソース・コードを見ると我慢できません。何かのついでに、重複している行を削除してしまいます。しかし、そんなことをすると、まったく関係のない機能が動作しなくなる可能性があるのです。
当然、どこかにバグは存在します。自分が作ったプログラムのバグではなく、コンパイラの不良、あるいはOSのバグの可能性もあり得ます。1行削除したため生成されるオブジェクト・コードの大きさが変わって、いままで隠れていた不良が表面化することもあるでしょう。どこかにバグがあるのですが、来週出荷しなければならないという大混乱期は、出荷することが最大の目的です。悠長に、どこにバグがあるのかを丹念に検討している時間はありません。
ベテランのソフトウェア技術者は、正常に稼働しているプログラムに対して、上記の「重複行の削除」のようなことはしません。それは、「機能退行」を恐れるためです。機能退行という名のパンドラの箱は、絶対に開けてはなりません。パンドラの場合、最後に「希望」が残っていましたが、ソフトウェア開発の場合、「分析困難なバグ」が残るだけです……。
修正している時間がないため、未修正のままバグや時間切れのために実装できなかった機能を「バックログ」と呼びます(ある意味、「借金」です)。10年前のアメリカの統計情報によると、PC用のプログラムで19カ月分、マイクロ・コンピュータ系で26カ月分、メインフレーム系ソフトウェアで46カ月分のバックログがあるそうです。積み残しの機能も含めた値なので、バグだけとはいい切れませんが、それにしても、物すごい量です。恐ろしいことに、年月が経過するに従って、量が増えているそうです。これは、ソフトウェア技術者にとっての“恐怖”でしょう。(次回に続く)
Copyright © ITmedia, Inc. All Rights Reserved.