ソフトウェア品質

ソフトウェア品質（ソフトウェアひんしつ、英: Software quality）は、ソフトウェアの品質を指し、プログラマの観点からはソースコードの品質、エンドユーザーの観点からはアプリケーションソフトウェアの品質を意味する。 ソフトウェア品質の定義は様々である。ジェラルド・ワインバーグは著書 Quality Software Management: Systems Thinking v. 1 で「品質とは誰かにとっての価値である」と書いている。この定義は品質が本来主観的なものであることを強調している。同じソフトウェアであっても人によって品質の感じ方は全く異なる。この定義の利点は、ソフトウェア開発チームに「このソフトウェアは誰のために作っているのか?」とか「彼らにとって価値とは何か?」といった疑問を抱かせる点にある。 品質を「目的への適合性」と定義する場合、品質を測定するのに使うべき尺度（属性）を決定する際に、そのソフトウェアの目的を考慮する必要があることを意味する。 商品品質 要求仕様やプログラム仕様への適合性（後述する「ソフトウェアの信頼性」に関連する） スケーラビリティ 正当性 完全性 バグが無いこと フォールトトレラントシステム 拡張性 保守性 ソフトウェアドキュメンテーション コンピュータにとっては、きれいに書かれたソースコードという概念には意味が無い。しかし人間にとっては、プログラムがどう書かれているかはそれを保守する際に重要な意味を持つ。様々なプログラミング作法は、可読性や言語固有の規約を強調してソースコードの保守性を向上させ、デバッグや修正を容易にする。他にもうまく書かれたコードという概念には、コードが管理可能な部分に論理的に分割されていることなども含まれる。 可読性 ソフトウェア保守、ソフトウェアテスト、デバッグ、バグ修正、改造、移植の容易性 複雑すぎないこと リソース（記憶装置、CPU）を過度に必要としないこと コンパイルやlintでの警告数 適切なコメント 品質向上の手法: リファクタリング デビッド・スコット・バーンスタインは、レガシーコードからの脱却で良いソフトウェアの土台となる5つのコード品質として以下を挙げ、CLEANと名付けている。 Cohesive（凝集性）：特性が明確に定義されているべき Loosely Coupled（疎結合）：はっきりした責務を担うべき Encapsulated（カプセル化）：実装は隠蔽されるべき Assertive（断定的）：オブジェクトの状態は自分自身が管理すべき Nonredundant（非冗長）オブジェクトの定義は一度だけにすべき ソフトウェアの信頼性（reliability）は、ソフトウェア品質を測る重要な尺度であり観点である。品質（quality）という用語には主観的（定性的）要素が含まれるが、ソフトウェアの信頼性は客観的な基準で測定可能である。このような測定基準を一般にソフトウェアメトリックと呼ぶ。ソフトウェアの信頼性改善の実際の作業は、ソフトウェア開発を扱いやすく理解可能なものにするという圧力によってなされてきたもので、収益を求めてのものではない。しかし、ソフトウェア産業の成熟と共に、ユーザーがソフトウェア製品に期待する品質レベルは高くなり、信頼性の確保が収益に繋がるという状況ができつつある。 組み込みシステムでは、ソフトウェアの問題は単なる不便以上の問題を生じる。場合によっては人命にも関わる。その原因はユーザインタフェースの貧弱さから直接的なバグまで様々である。多くの人命に関わったプログラムのバグについては、Levenson の論文 Medical Devices: The Therac-25 に詳しい。結果として、ソフトウェアの信頼性に関する要求仕様が生まれた。アメリカ合衆国では、アメリカ食品医薬品局 (FDA) と連邦航空局 (FAA) がソフトウェア開発についてその種の要求仕様を策定している。 現代の工学分野の技術をソフトウェア開発に適用するため、ソフトウェアの品質を客観的に判断する方法が必要とされる。これは、誰が見てもコンピュータソフトウェアは予定通りの動作をしないという一般的観測結果に基づいた動機である。言い換えれば、ソフトウェアは何らかの問題を抱えていると不適当な動作を見せる。その原因は処理中のデータの問題だったり、ソフトウェアが動作するハードウェアの問題だったり、操作者などがそのマシンに悪影響を及ぼしたためだったりする。経済活動や生産プロセス、生命維持に直接関わるシステムなどのより重要なソフトウェアでは、ソフトウェアの信頼性を評価する必要性も大きくなる。 あるソフトウェアアプリケーションの重要さの度合いによらず、ソフトウェアは我々の生活のあらゆる面に深く浸透している。我々の社会基盤がソフトウェアに依存している限り、この浸透は続くと予想される。社会基盤がソフトウェアへの依存を強めるにつれて、ソフトウェアの信頼性のレベルの向上が求められるようになってきた。すなわち、ソフトウェアは意図したように動作すべきであり、常に改良が求められる。 前節の循環しているような文章は偶然ではない。これはソフトウェアの信頼性の測定に関する基本的問題を表している。信頼性は判断が難しく、特に事前にソフトウェアがどう使われるかを正確に想定することは難しい。問題は、従来ならば人間が担っていたであろう役割をソフトウェアが代替しているという共通の概念的錯覚から生じる。この問題は2つのレベルに分けられる。第一に、最新のソフトウェアは人間にはできないようなことを実行し、特に人間に比較するとその信頼性は非常に高い。第二に、ソフトウェアは人間の精神的な面を代替することはできない。つまり、融通を利かせるとか、常識を働かせるとか、概念的なことを理解する能力はない。 それにもかかわらず、多くのソフトウェアプログラムは何らかの目的を持つと見なすことができる。目的が明確に定義されるならば、ソフトウェアにある環境であるデータを与えたときに、予測される結果と実際の結果を比較することでその信頼性を客観的に判断することができることが期待される。しかし今のところ、プログラムに環境とデータを与えたときに、予測される結果や実際の結果を正確に求めることが可能かどうか定かではない。そして、そのような手段がなければ、プログラムの信頼性を確実性をもって決定することはできない。 しかし、実際のプログラムやプログラムの理論的記述について、その環境や入力による振る舞いを明らかにしようという試みはこれまでも数多くなされてきた。ソフトウェアの信頼性を改善するためのその種の試みは、実際のソフトウェア開発では様々な工程（要求仕様、設計、プログラミング、テスト、実行時評価）に適用される。ソフトウェアの理論的な信頼性の研究は、主に正当性の概念を扱い、形式言語やオートマトンといった計算機科学の数学的分野から派生している。 プログラムの開発者が「どう動作すべきか」を事前または開発中に詳細に把握していなければ、それが望ましい動作をすることは期待できない。どの程度詳細に把握すべきかは議論の余地がある。完璧な詳細さという考え方は魅力的だが、実際問題としては現実的ではない。なぜなら、「こうあるべき」という要求仕様は試行錯誤的に変化するものだからである。 事前に要求仕様をうまく記述できるかどうかは信頼性の分かれ目であり、要求仕様の形式化（モデリング言語などの利用）が採用されるのはそのためである。また、プログラマ以外の専門家でない人々にそのソフトウェアの振る舞いを正しく伝達する手段でもある。実際には、プログラマも「どう動作するか」を事前に把握していないことが多く、それが要求仕様に関する議論を困難にしている。 要求仕様がプログラムがすべきことを記述するのに対して、ソフトウェア設計ではプログラムがそれをどのようにすべきかを記述する。設計工程の有用性には異論もあるが、信頼性の観点からはソフトウェア設計工程の重要性が指摘されている。ソフトウェア設計では、ソフトウェアを部品にわけ、それらのすべきことを記述する。そのようにしてプログラムを細分化していき、部品群が全体として正しく機能するようにする。 高レベルな設計の目的は次のようになる。アーキテクチャ上の問題やプログラムの概念や構造に関する問題を実際のデータ処理に関わるコード上の問題と分離する。ソフトウェアコンポーネントを細分化することで個々のコンポーネントの開発に関わるものに制約を与え、結果としてバグを作りこみにくくする。コンポーネント間のインタフェースを定義することで複数のチームがそれぞれ別のコンポーネントを並行して開発できるようにし、各チームがその役割を認識できるようにする。また、実装言語を指定せずにプログラムの機能を記述することで、言語に依存した偏向を排除する。 プログラミング言語の開発史は、プログラムの複雑さを克服する試みの歴史であり、言語の発展がなければプログラムはその規模に対して指数関数的に理解が困難になっていただろう。プログラミング言語はコンピュータにより多くの仕事をさせるべく進化したと言う事もできるが、これは同じことを別の観点で言っているだけである。プログラム全体の構造や機能を見失うと、バグを見逃すことが多くなる。従って、よい言語を使えば理解が容易になり、バグも減らせる。 言語はソフトウェア設計の意図していることをなるべく簡単に記述できるよう進化してきた。文、サブルーチン、ファイル、クラス、テンプレート、ライブラリ、コンポーネントといった概念の導入により、プログラムの記述が抽象化・階層化・モジュール化され、コードの理解が容易になってきた。 さらに言語の進化によってデータの構造や利用に関しても正確な制御が可能となってきた。 プログラムが要求仕様などの通りに動作するかを判定するのがソフトウェアテストである。単体テストは、プログラムのごく一部（多くの場合1つのサブルーチン毎）を対象に仕様通りに動作することを確認する。結合テストは、より大きなモジュール単位での動作を確認する。 実行時の信頼性の判定はテストと似ているが、その観点はより単純であり、性能、他のコードとの相互運用性、特定のハードウェア構成で動作可能かといった点で評価される。 ソフトウェア品質要因は機能面以外の要求仕様と考えられるが、顧客との契約に明記されることは少ない。しかし、ソフトウェアの品質を強化することは望ましい。 以下に主なソフトウェア品質要因を列挙する。 理解可能性(Understandability) 製品の目的が明らかなソフトウェア製品は理解可能である。この要因は単に目的だけではなく、設計文書やユーザー文書が容易に理解可能な形で書かれていることも含む。これは想定されるユーザーによっては重要な要因となる。例えば、ソフトウェア開発者向けのソフトウェア製品なら、素人が理解可能である必要はない。 完全性(Completeness) 製品が単独で機能できること、その機能が目的に対して十分であることを意味する。例えば、外部のライブラリを必要とするなら、少なくともそのライブラリの入手方法を付属文書などで示さなければならないし、バージョンなどの必要な情報も示す必要がある。 簡潔性(Conciseness) 無駄な情報がないことを意味する。メモリ容量が限られている環境では重要である。また、コードの行数を削減することも様々な意味で重要である。同じ機能を実現するコードが繰り返し出現する箇所をサブルーチン化することで改善される。また、文書についても同様のことが言える。 移植性(Portability) 様々なコンピュータ構成で容易に動作することを意味する。ハードウェアの違い（PC と Mac など）に関する移植性と、OSの違い（Mac OS X と GNU/Linux など）に関する移植性がある。 一貫性(Consistency) 記法や用語が一貫していることを意味する。 保守性(Maintainability) 新たな要求を満たすために改良する際の容易さを意味する。保守性の良いソフトウェア製品は、文書が完備されており、複雑でなく、メモリ容量や性能面で余裕がある必要がある。 試験性(Testability) 受け入れ基準が明確で性能評価が可能であることを意味する。設計段階での組み込みが重要となる。また、複雑な設計は試験性の低下を招く。 ユーザビリティ(Usability) ユーザーにとって便利で実用的であることを意味する。特にユーザインタフェースが重要となる。 信頼性(Reliability) ユーザーに影響するエラーを防ぎ、目的の機能が十分に実現されていることを意味する。これには機能をある時間内に実施できるという面も含まれる。また、必要とされたときには、エラーが発生しても停止しないで動作し続けることが要求される。これを堅牢性(ロバストネス(Robustness))とも呼ぶ。 構造化の度合い(Structuredness) 構成部品が一様なパターンとなっていることを意味する。構造化言語（Pascalなど）で書かれたソフトウェアはこの特性を満足する。 効率性(Efficiency) 目的達成の際にリソースを無駄に消費しないことを意味する。この場合のリソースとはメモリ使用量とプロセッサ使用時間である。 セキュリティ(Security) 不正アクセスからデータを守り、悪意ある操作に耐性があることを意味する。認証機構、アクセス制御、暗号化などのコンピュータセキュリティ機構の有無に関係する。 ソフトウェア品質の測定には様々な観点がある。しかし、万人が納得する測定の観点は珍しい。人によってはソフトウェア品質の定量的測定が必須であるとされるが、他の人は定量的測定が有効な場面は限られると考えており、そのため定性的測定を好む。真に望ましい測定を行うことの難しさはソフトウェアテストの分野の権威が何人か書いている。例えば、Cem Kaner（英語版） の Software Engineering Metrics: What Do They Measure and How Do We Know? と Douglass Hoffman The Darker Side of Metrics がある。 よく使われるメトリックの例として、検出バグ数がある。バグの少ないソフトウェアはバグの多いソフトウェアに比べて品質が高いとされる。以下のような質問の回答を考えることで、このメトリックが特定の場面で役立つかどうかを考えることができる。 どんなバグが多いのか? ソフトウェアの目的の違いを考慮しているか? ソフトウェアの規模や複雑さの違いを考慮しているか? バグの重大性を考慮しているか? 障害の重大性やユーザーへの影響によって重み付けしているか? もし重み付けしていない場合、100件のバグと1000件のバグのどちらが品質が良いのかをどう判断するのか? 検出バグ数が時と共に減っている場合、その意味をどう判断するか? 例えば、その製品が従来よりも品質が良くなっていると判断できるのか? あるいは、改造を以前ほどしなくなっただけか? あるいは、評価を行う者が変わって以前よりバグを検出できなくなっただけか? あるいは、チームが意図的にバグ数の報告を少なくしようとしているのか? 最後の質問は、特に管理上の難しさを示している。ソフトウェアは人間が作るものであるため、ソフトウェア品質のメトリックは、ある意味で人間の振る舞いを測定することである。チームがバグを過少に報告することで何らかの利益が得られるなら、そのチームは少なくバグを報告する傾向が強くなる。バグ追跡システムを出し抜く方法を見出したり、4、5件のバグを1件の報告にまとめてしまったり、些細なバグを報告しないことで作業量を減らすといったことである。意図したとおりの測定をすることは難しい。特にプログラマや評価者が、意識的か無意識的かに関わらず、測定そのものにゲームとしての誘因を見出さない場合、その傾向がある。例えば、品質保証部門がそのチームの過去のバグ傾向などから新たなソフトウェア開発におけるバグ収束曲線を予測し、実際のバグ報告とその予測を比較することで残存バグ数を見積もるとする。予測が正確であれば学習効果によって実際に報告されるバグ数は予測よりも少なくなる。予測通りのバグ数の報告が無ければ出荷が認められないとすれば、チームがバグ報告を過少に行うことはなくなる。しかし、予測が不正確であれば、実際のバグ数が予測を超過することになり、様々な目先の利害関係からバグを過少に報告するということが発生しうる。 ソフトウェア品質要因はその記述が曖昧であるために測定できない。しかし、機能以外の要求仕様を満たしているかどうかを明らかにするためには、何らかの測定が必要となる。例えば、信頼性はソフトウェア品質の要因のひとつであるが、そのままでは評価できない。しかし、信頼性という属性に関連して測定可能なものが存在する。例えば、障害発生間隔 (MTBF)、障害発生率、システムの可用性などである。同様に、移植性の属性はプログラム内のプラットフォーム依存の文の数で表される。 ソフトウェア品質要因の評価に使われるスキームを以下に示す。それぞれの要因（特性）について、関連する質問群がある。こういった質問への回答に基づいて一種の採点を行うことができ、それによって品質上の特性が定量化される。 理解可能性 変数名はその機能や構造をうまく表現しているか? コメント文を読んでそのコードの意味が明確に理解可能か? データ構造の機能は適切に分割されているか?（構造体が単なる無関係なデータの寄せ集めになっていないかなど） 完全性 通常のシステムには備わっていないサブプログラムが含まれているか? 必要とされるパラメータが全て揃っているか? プログラムに入力しなければならないデータが全て揃っているか? 簡潔性 実行されないコードを含んでいないか? 冗長なコードはないか? ループの外で実行してよいコードをループ内で無駄に複数回実行するようになっていないか? 分岐判定が複雑すぎないか? 移植性 特定のプラットフォームにしかないシステムやライブラリに依存していないか? 機種依存コードが明確化されているか（コメント、ifdefでの切り分けなど）? エンディアンや文字コードに依存していないか? 一貫性 1つの変数名を様々な用途に流用していないか? 定数の表現は一貫しているか? 同じ演算を一貫した表現で行っているか? 字下げスタイルは一貫しているか? 保守性 メモリ容量の一部を将来の拡張のために取ってあるか? 各モジュールの凝集度は適切か? データ構造の変更に容易に対処可能か? 機能の変更にあたって全体の再構成を必要とするか（変更の内容にもよる）? 試験性 コードの構造が複雑でないか? 詳細設計段階で明確な擬似コードが提示されているか? その擬似コードの抽象レベルはどうか? マルチタスクやマルチスレッドの場合、適切なテストが行える手段が整っているか? ユーザビリティ GUIが使われているか? 適切なオンラインヘルプが備わっているか? ユーザマニュアルは提供されるか? エラーメッセージは分かりやすいか? 信頼性 限界値のチェックは行われているか? ゼロ除算が起きないようになっているか? 例外処理を行っているか? 構造化の度合い モジュール分割の大きさは適切か? モジュール間の制御の渡し方は明確で、しかも例外なく守られているか? 効率性 実行時間は最適化されているか? 繰り返し使われるコードのブロックはサブルーチン化されているか? メモリリークが起きないか? セキュリティ 不正なアクセスからデータやプログラム自身が守られているか? セキュリティポリシーを操作者が設定可能か? 適切なセキュリティ機構を備えているか? そのセキュリティ機構の実装は正しいか? 攻撃を想定した設計になっているか? 脆弱性の原因となるような問題（バッファオーバーラン、書式文字列問題など）をはらんでいないか? ソフトウェアの技術的品質に加えて、エンドユーザーの経験もソフトウェアの品質を決定する。この種のソフトウェア品質をユーザビリティと呼ぶ。ソフトウェア製品のユーザビリティを定量化することは困難である。ユーザビリティを定性的に明らかにするための重要な質問を以下に列挙する。 ユーザインタフェースは直観的か? 単純な操作はやさしいか? 複雑な操作は適切か? エラーメッセージは分かりやすいか? ウィジェットは期待通りに動作するか? 文書は十分か? ユーザインタフェースの反応性は良いか? また、有料か無料かを問わず、サポートが得られるかどうかもソフトウェアのユーザビリティを左右する。