公開: 2021年10月19日
更新: 2021年10月20日
ソフトウェア技術の革新で、アジャイル開発の実践に最も寄与しているものは、オブジェクト指向設計法とそのプログラミング環境である。このプログラミング環境には、プログラミング言語とコンパイラ等の言語処理系だけでなく、汎用ライブラリの整備なども含まれる。特に、プログラミング言語においては、Java系言語の普及が大きく寄与している。Prologのような論理型プログラミング言語や、MLやErlangのような関数型プログラミング言語などの非手続き型言語も提案されているが、実用的なソフトウェアの開発では、応用例は多くない。従って、非手続き型言語系ではプログラミング環境も、特にライブラリの整備は、C++やJavaなどのJava系の言語ほど進んではいない。
Java系の言語を使ってソフトウェアを開発する場合、オブジェクト指向開発の特徴であるクラス・ライブラリの選択や準備が重要になる。ソフトウェアの各応用分野別に準備されているクラス・ライブラリを適切に選択することで、ソフトウェア開発を著しく効率化できる。また、応用分野が特別な問題分野になると、その応用分野に特有なクラス・ライブラリを準備することが重要になる。そのようなクラス・ライブラリの開発には、優秀な技術者集団が必要になる。
クラス・ライブラリの設計は、オブジェクト指向設計では、プログラミングのための基本的なアーキテクチャ(基本構造)を決めることになるので、長期間にわたり利用するソフトウェアの拡張性や変更のし易さに大きく影響を与える。最近のソフトウェアでは、開発期間に比較して、運用期間中の移植、修正、機能追加、拡張等の作業に投入する工数の比率が大きくなる傾向がある。このため、ライブラリの設計においてしっかりと情報隠ぺいがなされ、カプセル化されたクラスを設計できるかどうかが重要になる。
Java系言語での開発の場合、既存の汎用開発環境が提供されている。特に、オープンソースで提供されている開発環境もあり、それらの候補の中から、最も適切な開発環境を選定することが重要になる。また、形式手法の応用が要求される開発では、仕様記述言語、仕様チェッカ(証明器を含む)などの選定とともに、図式による仕様記述の入力が可能な例もあり、どのような図式を利用するのかも、重要な問題になる可能性がある。
多数の小規模なチームによって同時並行的に開発作業が進むとき、全体の進捗管理は複雑になる傾向がある。さらに、個々のチームが担当する小さな機能の開発活動の間には、時として相互依存関係が存在する例も多く、あるチームの作業遅れが、数多くの他のチームの開発作業に直接的な影響を与えることも珍しくない。このような場合、開発中の機能間の関係などを明示して、その依存関係に基づいた進捗管理を実施するためのツールが開発環境で提供されることが望まれる。部分的には、そのような機能はすでに提供されている。
問題解決法としては、1960年からの半世紀、大きな進歩はなかった。ただし、コンピュータ科学分野の教育カリキュラムが整備されたため、多くの専門家が問題解決の基本的な方法を知るようになった。1960年頃から1970年頃にかけて、ソフトウェア工学の黎明期に、ダイクストラが提唱し、ビルトによって確立された『構造化プログラミング』によって、トップダウンに問題を考える方法、さらに段階的に詳細化してゆく方法が一般的に教育されるようになった。
トップダウン法は、ローマ人が考案した「分割統治」の方法を複雑な問題の解決に応用しようとする考え方で、古くから数学では利用されてきた方法の一つである。さらに、ジーコブスキーが一般意味論 で提唱した段階的詳細化の方法論を取り入れ、古典的な構造化プログラミングの方法が確立された。似たような方法が、プロジェクトマネジメントの分野でも、WBS(Work Breakdown Structure)法として、作業の詳細化のための方法として提案されている。
アジャイル開発におけるシステムの機能項目の分解や、それに基づく小さなチームによる小さな機能の開発は、この分割統治の考え方を取り入れて、ソフトウェア開発の失敗のリスクを低減しようとしている。この小さな機能インクリメントに分解して、リスクの小さな作業を繰り返すと言う方法は、1970年代の後半に、ミルズが「段階的開発法(incremental development)」として提案したものである。
例として、あるシステムの開発をウォーターフォール開発で実施しようとすると、仕様定義、設計、実現、機能テスト、システムテストの工程は、図1上段のPERT図に示されるように計画され、実施されることになる。
この図1のような開発は、仕様定義の開始から実現の完了まで、途中の段階で仕様の正しさや設計・実現の正しさをレビュー以外の方法で検証することは困難である。このことから、機能テストの開始まで、開発プロジェクトが本当に成功しそうかどうかは、開発チーム外部の人間には全く分からない。つまり、リスクは隠された状態にある。
ミルズの提唱した段階的開発では、このウォーターフォール開発を細かく分割することによって、図2のPERT図に示されるような複数の小工程に分解する。図2においては、図1で仕様定義とされた工程が、大きくシステム仕様定義の工程と、4つの分解された機能項目別の仕様定義の工程に分解されている。さらにそれに続く設計、実現、機能テスト、統合テストも、機能項目別の作業に分解されている。
ここで、図1上段の仕様定義をDsと表記し、図1下段と図2のシステム仕様定義をDssys、分割された個々の要素の仕様定義をDscomp1からDscomp4までの4つの表記で表す。このとき、任意の作業または工程xの工数を与える関数をw(x)で表現する。このとき、以下の関係が成立する。
w(Ds) = w(Dssys) + w(Dscomp1) + w(Dscomp2) + w(Dscomp3) + w(Dscomp4) (1)
これは、作業として見たとき、Dsと、DssysおよびDscomp1からDscomp4までの全ての作業の内容が、ほぼ等価なものであることを仮定している。
さらに、図1上段の設計をDdと表記し、図1下段と図2の分割された個々の要素の設計をDdcomp1からDdcomp4までの表記で表す。このとき、任意の作業または工程xの工数を与える関数w(x)を用いれば、以下の関係が成立する。
w(Dd) = w(Ddcomp1) + w(Ddcomp2) + w(Ddcomp3) + w(Ddcomp4) (2)
ここでも、作業として見たとき、DdとDdcomp1からDdcomp4までの全ての作業の内容が、ほぼ等価なものであることを仮定している。
同様にして、図1上段の実現をDiと表記し、図1下段と図2の分割された個々の要素の実現をDicomp1からDicomp4までの表記で表す。また、図1上段の機能テストをTfと表記し、図1下段と図2の分割された個々の要素の機能テストをTfcomp1からTfcomp4までの表記で表す。このとき、任意の作業または工程xの工数を与える関数w(x)を用いれば、式(3)および式(4)の関係が成立する。
w(Di) = w(Dicomp1) + w(Dicomp2) + w(Dicomp3) + w(Dicomp4) (3)
w(Tf) = w(Tfcomp1) + w(Tfcomp2) + w(Tfcomp3) + w(Tfcomp4) (4)
ここでも、作業として見たとき、DiとDicomp1からDicomp4までの全ての作業、およびTfとTfcomp1からTfcomp4までの全ての作業の内容が、ほぼ等価なものであることを仮定している。
現実を考えると、式(4)は、成り立たない。それは、機能テストで問題が発見された場合、その修正作業が実施されるからである。厳密に考えると、式(4)は、式(5)のようになる。
w(Tf) = w(Tfcomp1) + w(Tfcomp2) + w(Tfcomp3) + w(Tfcomp4) (5)
ここでは、式(4)の左辺と右辺の差は、無視できる程度であると仮定する。この差は、図1のウォーターフォールの場合、システムが1つの完結したものとして、設計、実現されているため、機能テストで誤りまたは設計から修正すべき問題が発見されると、その修正がシステム全体に波及する例があり、テストで問題が発見された処理だけでなく、他の部分の設計変更、プログラムの修正も必要になる例があるからである。
最後に、図1上段のシステムテストをTsと表記し、図1下段と図2の分割された個々の要素の統合テストをTicomp1からTicomp4までの表記で表す。このとき、任意の作業または工程xの工数を与える関数w(x)を用いれば、式(6)の関係が成立する。
w(Ts)< w(Ticomp1) + w(Ticomp2) + w(Ticomp3) + w(Ticomp4) (6)
ここでは、作業として見たとき、TsとTicomp1からTicomp4までの全ての作業の内容について、以下の関係が成立していることを仮定している。
w(Ticomp1) = w(Tscomp1) (7)
w(Ticomp2) = w(Tscomp2) + w(Ticomp1) (8)
w(Ticomp3) = w(Tscomp3) + w(Ticomp2) (9)
w(Ticomp4) = w(Tscomp4) + w(Ticomp3) (10)
ただし、Tscomp1は、Tsのうちのcomp1の要素に関係するテストだけを選択したものであるとする。Tscomp2からTscomp4までも同様とする。従って、システム統合をTiと表現すると、式(11)が成立する。
w(Ts)= w(Tscomp1) + w(Tscomp2) + w(Tscomp3) + w(Tscomp4) + w(Ti) (11)
このことから、アジャイル開発における統合テストAstの総工数に関して、式(12)が得られる。また、Tiについて式(13)が成り立つとする。
w(Ast)= 4w(Tscomp1) + 3w(Tscomp2) + 2w(Tscomp3) + w(Tscomp4) (12)
w(Ti)= w(Ticomp1 + Ticomp2 + Ticomp3 + Ticomp4) + w(Ticomp1) + w(Ticomp2) + w(Ticomp3) + w(Ticomp4) (13)
これより、
w(Ast) >= w(Ts) (14)
が導かれる。テストの工数から見れば、アジャイル開発は、ウォーターフォール型開発より工数が増加する傾向がある。
アジャイル開発では、仮に一部の機能項目別の開発工程で失敗が生じても、作業のやり直しは、その部分の開発だけに限定でき、これによる工数の増大を最小限にすることができる。また、各機能項目別開発工程の最後の統合テストでは、それまでの開発で実現できているソフトウェアが統合され、テストされるので、その実現品質を第三者が見ることが可能になる。これは、開発中のソフトウェアの品質の可視化になると言う利点もある。
ただし、図1と図2を比較すれば分かるように、図2の工程の方が、重複が多く、冗長であり、結果として投入する工数は、上述したように増大する結果になる。つまり、リスクをしっかりと管理でき、失敗がなければ、式(13)の関係からウォーターフォール開発の方が、効率は勝っている。ではなぜ、それでもアジャイル開発なのか。その理由は、前述した経済のサービス化による変化の速さが問題だからである。つまり、開発の途中であっても社会の様々な変化によって、システムへの要求が変化する可能性があるからである。
分解されたシステムの機能項目のうちのいくつかは、その実現が完了するまでに仕様そのものを変える必要性が生じる。そのため、仕様定義から作業をやり直さざるをえない状況になる。このとき、ウォーターフォールでは、全ての開発を最初から見直し、やり直すことが必要になる。段階的開発を採用しているアジャイル開発であれば、このやり直しの対象範囲を、やり直しが必要な機能項目だけに限定することができる。この歩留まりの良さが、現代のソフトウェア開発では決定的に重要である。
仮に、図2に示された例の第3番目の要素comp3に対する要求が、社会の変化によって大きく変化したと仮定する。この場合、ウォーターフォール型開発では、新しいシステムの開発をやり直すことになる。つまり、全体の工数w(Tw)は、式(15)で与えられる。
w(Tw) = w(Ds) + w(Dd) + w(Di) + w(Tf) + w(Ts)
+ w(Ds’) +w(Dd’) + w(Di’) + w(Tf’)+ w(Ts’) (15)
これに対して、アジャイル開発では、comp3の修正だけで解決できるため、全体の工数w(Ta)は、式(16)で与えられる。
w(Ta) = w(Dssys) + w(Dscomp1) + w(Dscomp2) + w(Dscomp3) + w(Dscomp4)
+ w(Ddcomp1) + w(Ddcomp2) + w(Ddcomp3) + w(Ddcomp4)
+ w(Dicomp1) + w(Dicomp2) + w(Dicomp3) + w(Dicomp4)
+ w(Tfcomp1) + w(Tfcomp2) + w(Tfcomp3) + w(Tfcomp4)
+ w(Ticomp1) + w(Ticomp2) + w(Ticomp3)
+ w(Dscomp3’) + w(Ddcomp3’) + w(Dicomp3’) + w(Tfcomp3’)
+ w(Ticomp3’) + w(Ticomp4) (16)
さらに、式(15)と式(16)の間には、以下の関係が仮定できる。すなわち、
w(Dscomp3’) << w(Ds’) (17)
w(Ddcomp3’) << w(Dd’) (18)
w(Dicomp3’) << w(Di’) (19)
w(Tfcomp3’) << w(Tf’) (20)
w(Ticomp3’) + w(Ticomp4) << w(Ts’) (21)
である。このことから、開発中にシステムの要求に変更が発生するリスクが一定程度存在すると考えられる場合、ウォーターフォール型の開発よりも、アジャイル型の開発の方が、総工数の期待値は小さくなる。つまり、開発効率が向上する。同様の議論は、開発期間についても可能である。