アジャイル開発者の習慣-acts_as_agile

第5回設計を進化させる

いまやもっとも美しい生き物はカメだろう。

――映画『エル・マリアッチ』

はじめに

本連載ではアジャイル開発を「アジャイルに開発する人たち(アジャイル開発者)が開発するからアジャイル開発」と考え、アジャイル開発者に必要なスキルを磨くための習慣を紹介しています。

今回は「変化」に対するアジャイル開発者の向き合い方を、⁠設計を進化させる」という習慣を通じて紹介します。これは「進化的設計」というキーワードで知られています。

連載の第1回で説明したように、アジャイル開発の目的は「ソフトウェアの価値の総量を最大化させる」ことです。変化に適応することは、このアジャイル開発の目的を達成するにあたって欠かせないものです。過去の連載で紹介してきた習慣のいずれもが「変化に適応する」ことと関係しています。

今回はまず、アジャイル開発者にとっての変化について簡単に説明します。それから、変化に適応することのソフトウェア設計への応用として「進化的設計」の考え方を紹介します。そして、この進化的設計を実践するために身につけるべき習慣を紹介します。

「変化ヲ抱擁セヨ」

変化はアジャイル開発者にとってとても強い関心事です。本連載の第2回でも、アジャイル開発とは「状況の変化に迅速に適応しながら開発を持続させていくこと」だと述べました。

アジャイル開発と変化

そもそものアジャイル開発のムーブメントの始まりである『XP エクストリーム・プログラミング入門』注1の原著の表紙には「Embrace Change(変化ヲ抱擁セヨ⁠⁠」と謳われています図1⁠。

図1 ⁠XP エクストリーム・プログラミング入門』
の原著の表紙
図1 『XP エクストリーム・プログラミング入門』の原著の表紙

これは「変化を嫌うのではなく変化が起こることを自然なこととして受け入れよう、というXPの基本姿勢」注2を表しています。

昨年末に刊行された『アジャイルプラクティス』注3にも「⁠⁠アジャイルである』とはつまり変化に対応することだ」とあります。

Mike Cohnは『Agile Estimating and Planning』注4という、見積りと計画をアジャイルにすることを扱った書籍で次のように述べています。

計画に対するアジャイルな態度とは「変更されてもかまわない」ではない。むしろ「積極的に変更したい」と思うのがアジャイルな計画である。

――Mike Cohn

かなり大胆ですね。

このように、アジャイル開発がことさらに「変化」にこだわるのはある意味当然ともいえます。というのは、アジャイル開発はその出自が従来型の開発――事前に策定した包括的で綿密な計画を絶対視する開発(=変化しないことを前提とする開発)に対するカウンターだからです。

変化の存在を認め、正面から向き合うというのがアジャイル開発者の基本的な態度です。

さまざまな変化

みなさんの中には(この連載も含めて)記事や書籍で語られる「変化」を、お客様から電話やメールで、あるいはマネージャから伝えられる「仕様変更」のことだととらえている方がいるかもしれません。

ソフトウェア開発の文脈でよく言及されるのは、上述したようなソフトウェアに対する要求の変化でしょう。確かに、仕様変更や要求の変化はプロジェクトへの影響も大きいですから、関心が高いのも当然です。

ですが、アジャイル開発で扱う変化は、こうした変化だけではありません。

ソフトウェア要求の変化

上で述べた「仕様変更」も、その背景にはさまざまな変化があります。たとえば法改正やビジネス環境の変化、お客様の組織改編、ベンダーの戦略転換などです。

アジャイル開発では、こうした変化に適応できることはもちろんですが、プロジェクトを進めていくなかでシステムに対して学んだことによる変化も取り入れていこうとします。

仕様の確認を進めていくなかで「そういうことだったんですね。じゃあ、ここはやっぱりこうしたいですね」といった意見が出てくる場面に遭遇したことはありませんか?

そのときに「それは変更できません」と答えるのではなく、実現させるための現実的な解決策を提示できるように努めるのがアジャイル開発者です。

組織やチームの変化

自分の所属する組織やチーム内でも変化は起こります。プロジェクトメンバーの増減や入れ替わりや、技術的な環境の動向、開発の進め方の改善も変化です。第1回で紹介した「ふりかえり」はこのレベルでの変化を促すためのプラクティスでもあります。

個人の変化

そして、個人のレベルでも変化は起きています。みなさんも経験があると思いますし、今現在も体験していると思います。

プロジェクトに対する理解は、チームで過ごしていく時間が経つにつれて、プロジェクトに参加した当初よりも深まっているはずです。プロジェクトで実現しようとしていること、採用している要素技術、一緒に働いているメンバーの振る舞い……(もちろん、程度の差はあります⁠⁠。日々の作業の中での個人的な「気づき」も立派な変化の一つです。

変化をシステムに反映させる

重要なので何度でも書きますが、アジャイル開発とはお客様に価値のあるソフトウェアを提供するために「状況の変化に迅速に適応しながら開発を持続させていくこと」です。

「変化に適応する」とは、ソフトウェア要求の変化から個人の変化まで、さまざまなレベルでのフィードバックや学習の結果、気づきといった変化をシステム(=仕組み)に反映させていくことです。

本連載では「仕組み」とは開発対象のシステムと、開発対象システムの開発プロセスのことでした。連載の第2回では、主に開発プロセスを育てることに重点を置いて説明しました。

今回は、開発対象システムを変化に適応させる方法、すなわちコードを変化に適応させていくためのマインドセット(心構え)とプラクティス(実践)を紹介します。そのためのキーワードが「進化的設計」です。

進化的設計

進化的設計(Evolutional Design)はMartin Fowlerが設計の終焉?という記事で提唱した考え方で、計画的設計(Planned Design)と対比されるものです。

計画的設計

計画的設計は、従来から広く行われている「ソフトウェアを作る際にまず設計を行い、それからコーディングする」という設計の進め方です。経験の豊富な開発者が設計を担当し、コーディングは別の開発者が担当するという作業分担がされることもよく見られます。

計画的設計はアジャイル開発者の間ではあまり評判は良くありません。というのも、変化への適応をあらかじめ予測して、設計の柔軟性として盛り込んでおかねばならないからです。

計画的設計では、事前の予測が当たれば変化が起きたときに変更コストを低く抑えられます。しかし、予測が外れた場合には設計をやり直すか、それが無理なら「Work Around」と呼ばれる場当たり的な対処が必要になってしまいます。⁠柔軟な設計」が逆に足手まといになった苦い経験をみなさんはお持ちではありませんか? その意味では計画的設計には「うまくいくかもしれないし、うまくいかないかもしれない」という投機的な側面があります。

そして往々にして変化とは予測もしない方向からやってきます。アジャイル界には「予測できるような変化は変化ではない」という格言もあるぐらいです[5]⁠。

計画的設計は「事前の大規模設計(Big Design Up Front:BDUF⁠⁠」や「前払いの設計」とも呼ばれます。

進化的設計

一方、進化的設計では「プログラミングを行いながらゆっくりと設計を進化させていきます。はじめから設計は存在しません。小さな機能をコーディングすることから始め、機能を追加していき、設計を洗練させていきます」注6⁠。

進化的設計は「はじめから設計は存在しない」という言葉から、いきなりコーディングを始める無統制で無秩序な「書いて直せ(Code and Fix⁠⁠」アプローチと混同されることも多いのですが、そうではありません。

進化的設計は「いま・ここ」で必要な分だけ設計することを繰り返します。計画的設計の設計コストを「前払い」するアプローチと対比させるならば、進化的設計は設計コストを「都度払い」するアプローチといえます。

また、進化的設計は「必要十分な設計(ENough design Up Front:ENUF⁠⁠」と呼ばれることもあります。

進化のメタファが伝えたいこと

進化的設計は、生物の進化をソフトウェアの設計に適用したメタファです。⁠進化」とは生物の種が時間の経過とともにその形や機能が変化していくことです。

単に「変化する設計」と言わずに「進化的設計」と少し気どった言い回しをするのは理由があります。アジャイル開発における設計の考え方には、変化すること以外の意味も含まれているからです。その主なものを3つ紹介します。

変化への適応の「結果」である

計画的設計と異なる進化的設計の一番のポイントです。進化的設計とは、時間の経過とともに状況の変化に適応して洗練させていった結果として実現された設計のことです。生物のさまざまな姿や生態も環境の変化へ適応した結果であって、当初から意図を持って「設計」されたものではありません。

進化的設計では、当初は思いも及ばなかった設計が実現されることは日常茶飯事です。実現した設計は、当初想定していた構造よりもシンプルになっているこもあれば、その逆の場合もあります。

これはプロジェクトでの作業を通じて得られた学習や気づきといったフィードバックをソフトウェアに反映させることで達成できます。設計が変化していく過程自身から学ぶことも多くあります。

各段階で「機能」している

進化的設計では、設計は時間とともに変化していきますが、その変化の過程であってもソフトウェアとして機能していなければなりません。進化のメタファでいえば、機能しない(=その環境で子孫を残せない)生物種は絶滅してしまいます。

これはソフトウェアでも同じです。変化のために機能が損われてしまっては、ソフトウェアとしての価値を提供できません。

「進歩」ではない

進化的設計も進化と同じく、設計の変化は「進歩」を意味するわけではありません。時間の経過と共に追加されていく構造もあれば、取り除かれる構造もあります(人類は進化の過程で尻尾が退化しています⁠⁠。

アジャイル開発の言葉でいえば「シンプル設計」の考え方がこれに対応します。

カメの設計

たとえばカメの設計を考えてみましょう。甲羅を背負ったカメは少し変わった姿をしていますが、次のような特徴があります写真1⁠。

写真1 ヘルマンリクガメ
写真1 ヘルマンリクガメ
  • 生物種として2億年にわたって存続している
  • 生息域は南極を除く全大陸に分布
  • 個体の寿命も長い

「未来に子孫を残す」という生物の機能からすれば、設計としてはかなりの成功例ではないでしょうか。あくまで例え話ですが、事前にカメのような形態の設計がここまで成功することを見通すのは難しそうです。

「固い甲羅で外敵から身を守りますが、移動速度が落ちます。事故でひっくり返った際は、運が悪ければ死にます」……少なくとも私がカメを事前設計したとしたら、設計レビューに合格する自信はありません[7]⁠。

習慣 #4 設計を進化させる

言い回しはしゃれている進化的設計ですが、特徴を整理すると実践するのは難しそうです。

進化的設計の特徴

  • 事前に大規模な設計をすることなく、
  • 時間の経過とともに変化に適応しながら、
  • 設計を洗練させつつ、
  • ソフトウェアとしての価値を提供し続ける

実はというか予想通りというか、これは簡単ではありません。しかし、実現するために必要なマインドセット(心構え)とプラクティス(実践)を継続させれば不可能ではないことは強調しておきます。順番に紹介します。

マインドセット「予測できないものが変化である」

進化的設計を実践するためには、まずマインドセットを切り替える必要があります。もう一度書いておきます。「変化とは、事前に予測できないから変化である」

そして、問題となるのは変化が起きてしまうことではありません。起きた変化に適応できないことが問題なのです。

設計コストを「都度払い」にする

ソフトウェアの設計に関していえば、事前に変化を見通すことではなく、変化が起きたときに迅速に対応できるようにすることが重要なのです。設計コストを「都度払い」にするとはそういうことです。

ですが、一朝一夕に設計コストを都度払いにはできません。都度払いにするための「仕組み」を育てる必要があります[8]⁠。今回紹介するプラクティスはそのためのものです。

マインドセット「それはユーザ価値を提供するか?」

後述する進化的設計のプラクティス、とりわけテスト駆動開発とリファクタリングについては「どこまでやれば適応できているのか?」を判断するのが難しかったり、議論になることがあります。究めようと思えばいくらでもやることはあり、熱中し過ぎるとビジネス価値を損うことにもなりかねません。

設計は完璧でなくてもよい

そんなときには行った設計や、取り組もうとする作業が「ユーザ価値を提供するか?」という視点から自分自身やチームに向けて問いを発してください。

書いたテストや施したリファクタリングが、その時点で常に完璧である必要はありません。現在の設計はいずれ変更されることになりますし、変更されるべきだからです。

視点を変えれば、ユーザ価値を提供できてさえいれば、設計は美しくなくても、完璧でなくてもかまわないのです。むしろ、あとで必要になったときに変更できるか――つまり、後述する3つのプラクティスをどれぐらい充実できているかを気にかけるべきです。

ユーザ価値の提供を判断する基準

また、ユーザストーリは、ユーザ価値を提供する基準となっているはずです。計画づくりは、提供するユーザ価値に優先順位をつける活動です。自分の作業を常にユーザストーリや計画と照らし合わせて、進路を間違えないように、間違えたときはすぐに軌道修正することを心がけましょう。

プラクティス「テスト駆動開発(TDD)」

ソフトウェアの設計を変更させる過程であっても価値を提供させ続けていくためには、現在のコードが壊れていないことを確認する手段としてのテストは欠かせません。

ですが、現在の設計がテストしづらい設計になっていると、テストすることもできません。TDDは進化的設計における最重要プラクティスです。

TDDは「都度払い」の設計

「どうすればテストを書けるコードにできるか?」を考えることは立派な設計作業です。

James ShoreとShane Wardenは『The Art of Agile Development』注9で、TDDのステップを従来の「レッド、グリーン、リファクタリング」から拡張し、最初のステップを「Think」すなわち「考えること」だと主張しています図2⁠。

図2 テスト駆動開発の4つのステップ
図2 テスト駆動開発の4つのステップ

これはTDDが単なるコーディングでなく設計作業であり、この設計がテストを書くたびに少しずつ行われていくということ、つまりTDDが都度払いの設計であるということです。

なお、テストのない既存コードをどうやってテストできるようにするかについては『Working Effectively with Legacy Code』注10がたいへん参考になります。

リファクタリングの安全ネット

テストケースは、後述するリファクタリングの過程で既存コードを壊していないことを保証する安全ネットでもあります。

テストを用意せずに設計を変更していくのは進化的設計ではありません。それは「書いて直せ」アプローチです。

プラクティス「リファクタリング」

リファクタリングはプログラムの外部から見た振る舞いを変えることなく、ソフトウェアの設計を改善していくことです。

リファクタリングでは、テストを実行させてコードを壊していないことを確認しながら一歩ずつ設計を変更していきます。

具体的なリファクタリングの手順は『リファクタリング』注11『パターン指向リファクタリング入門』注12を参照してください。

ちなみに『パターン指向リファクタリング入門』では、既存のコードにデザインパターンを適用させていく手法だけでなく、パターンから離れていく手法も扱っています。これは「進化は進歩ではない」とも呼応する考え方であり、興味深いです。

ここでは大規模なリファクタリングについてだけ簡単に補足しておきます。

大規模なリファクタリング

場合によっては、いくつものクラスが関わる大規模なリファクタリングが必要になることがあります。このときのポイントは「独断で一度に全部やってしまわないこと」です。

現行の設計から新しい設計への移行パスをチーム内で話し合い、一歩ずつ進めていくのが基本です。ホワイトボードなどを使って、設計の移行過程のイメージをチームで共有しておくことも大事です。

プロジェクトで作業していると時折、何もかも捨ててやりなおしたくなりますが、現在動作して価値を提供しているソフトウェアを大事にしてください(もちろん何事にも例外はあります⁠⁠。

プラクティス「継続的インテグレーション」

継続的インテグレーションは、自動化されたテストの実行を1日に何度も行うプラクティスです[13]⁠。

問題を早期に発見するために、プロジェクトで正とされるリポジトリの最新版ソースコードに対するビルドとテストを定期的に実施します。⁠定期的」の頻度はさまざまですが、最低でも1日に1回、できれば数時間から数十分に1回は実施します。

継続的インテグレーションでは、テストケースがなければ問題が発生してもそのことがわかりませんし、リファクタリングによって変更が行われなければ、頻繁に実施してもありがたみはありません。ほかのプラクティスと組み合わせることで威力を発揮するものです。

今では継続的インテグレーションのためのツールはさまざまなプラットフォーム向けのものが提供されています。私の周囲ではJavaではContinuumHudsonRubyではCruiseControl.rb.NETではCruiseControl.NETに定評があります。

連載の第2回では継続的インテグレーションの考え方を開発プロセスに適用する方法を紹介しました。こちらも参考にしてください。

今回のまとめ

今回の内容を図3にまとめます。今回紹介した「進化的設計」は開発対象システムを変化に適応させるための習慣でした。ここで連載第3回で紹介したことを思い出してください。アジャイル開発は自己相似形なのです。

図3 今回のまとめ
図3 今回のまとめ

進化的設計で実践しているプラクティスは、プロジェクトの開発プロセスのスケールや、プロジェクトに参加する開発者個人個人のスケールにも応用できる面があるはずです。

みなさんの開発現場が変化に適応できるようになるきっかけになれば幸いです。また次回お目にかかりましょう。acts_as_agile!

おすすめ記事

記事・ニュース一覧