ビヘイビア駆動開発 ― ウォーターフォールモデルからのステップ

ビヘイビア駆動開発(BDD:Behavior-Driven Development、振る舞い駆動開発ともいう)を実務に沿って簡単に紹介し、ソフトウェア開発プロセスに対してこの手法がどれほど有益かを説明します。

はじめに

BDDで重視しているのは、フィードバックループを最小限に短縮することです。BDDはソフトウェア開発手法の進化の中で、理論的に一歩前進したものといえます。本稿ではBDDの概念と、その原型のモデルを説明します。

ウォーターフォールモデル

ソフトウェア開発者や、エンジニア部門のマネージャー職に就いている人ならば恐らく、以下の図のようなウォーターフォールモデルはよくご存じでしょう。


注釈:
Waterfall model:ウォーターフォールモデル
System Requirements:システム要件定義
Software Requirements:ソフトウェア要件定義
Analysis:要求分析
Program design:プログラム設計
Coding:コーディング
Testing:テスト
Operations:システム運用

後に「ウォーターフォール」と名付けられたこのモデルは、Winston Royceが1970年に発表した、「Managing the development of large software systems」(大規模ソフトウェアシステムの開発管理)という論文の中で示されたのが最初です。この論文を通読して、モデルの趣旨を全体的に把握することをお勧めします。ほとんどの人は、このモデルが別の文献で引用されている部分だけを読んで、当時最善のものとして提示されたと捉えているのではないでしょうか。ところが実際は、論文の著者であるRoyce自身も、このモデルで開発プロセスの最後にテストを実施する点は大きな問題だと認識し、以下のように指摘していました。

このモデルの概念自体は正しいと確信している。ただし、上記のモデルを実践に移すことにはリスクがあり、失敗を招く恐れもある。(中略)開発サイクルの最後に実施するテストフェーズで初めて、机上の分析結果を実際のシステムの動作として、例えば処理実行タイミングの同期、ストレージの使用、データの入出力などの形で体験することになる。(中略)仕様変更の要求に応じると、壊滅的な結果になる場合が少なくない。設計のよりどころであり、システム全体の理論的根拠でもあるソフトウェア要件にほころびが生じるからだ。システム要件の変更を余儀なくされるか、仕様を抜本的に変更しなければならなくなるかのどちらかになる。事実上、開発プロセスが振り出しに戻り、開発スケジュールや費用は、予定の2倍にまで膨れ上がることもある。

今でも世界中の多くの企業でこのモデルに基づいたソフトウェア開発が進められていますが、これには多くの理由があります。ウォーターフォールは「一方向への流れ」を連想させる表現ですが、現実では通常、各フェーズ間にフィードバックループが発生します。このモデルは長い間運用されるうちに大幅に改善されました。フィードバックループは最小限に短縮され、プロセス全体の予測精度も向上しました。例えば開発者はプログラムを書いている時から、そのプログラムの動作を確認するのにどのくらい時間がかかるのかを気にするようになりました。他方、システムの設計段階でも、その設計は実際にプログラミングによる実装および検証が可能なのか、その工程のコストはどのくらいかかるのかを調べるようになりました。

ではここでフィードバックループに注目して、このループの工程を最小限に圧縮する手法を追求します。当初は、どう見ても無駄な作業を省くことを目指しましたが、そのうちに最適化できることが分かりました。こうして、旧来の手法で開発を進めていたころには想像もできなかったほどプロセスは高速化し、作業の質も向上しました。

最適化の第一歩:テストファーストプログラミング

最初の最適化は、コーディングとテストのフェーズに注目したものでした。従来の品質保証(QA)ベースの開発モデルでは、プログラマはコードを書き、何らかの方法でQAチームにそのコードを提出します。コードが動作するかどうか、プログラムの他の部分も問題なく動作するかどうかの検証結果を得るまで、まる1日、あるいは数日、数週間かかったものでした。この時点のコードには、往々にしてバグは残っています。従って仕事を終えたつもりになっていたプログラマも、再びプログラミングに戻って、見つかった問題を全て修正しなければなりません。


注釈:
Coding:コーディング
Testing:テスト
Bugs:バグ

いよいよフィードバックループを短縮するために、コーディングと検証を並行して進めます。コードを少し書いては、その範囲でテストを実行するといった具合です。するとテスト作業の中で、すばらしい副産物が得られました。テストスイートの自動実行です。システムの任意の部分の動作に対して、テストのスクリプトを書いておけば、テストをいつでも好きな時に実行できます。すぐに私たちは、なるべく安全に作業が進められるように、テストスイートでシステム全体の検証をカバーすることができないかと考えるようになりました。
コーディングの後にテストを実行するフィードバックループでは、プロセス改善に着手したこの時点でもやはり、少し時間がかかっていました。そこで流れを逆転させました。コードを1行も書かないうちから、先にテストの作成に取り掛かるのです。これでフィードバックループは大幅に短縮されましたし、試してみてすぐに、私たちはあらかじめ作成したテストに合格するために、必要なコードしか書かなくなったことに気付きました。


注釈:
Testing:テスト
Coding:コーディング

この手法をテストファーストプログラミングといいます。テストファーストプログラミングでプロジェクトを進める場合は、まずテストを書き、これを使って実装の要件を正しく「満たしている」ことを確認します。こうするとかなりの数のバグが減り、プログラマの生産性が上がって、チーム全体の作業のペースも上がるという効果につながります。

テスト駆動開発

この時点では、テストとコーディングを継続的にループとして実行するようになっても、プログラムの設計は昔と変わらず最初に実施しています。テストファーストプログラミングを実践して、コードの動作は直ちに確認しているものの、その一方で、(困惑するほど時間が経ってから)フィードバックループが発生することもあります。テストによる検証が困難、コーディングができない、パフォーマンスが悪い、いざ実装を試みると、同時に実装しようとしている別機能との連携がどうもうまくいかないなど、設計上の問題が後工程で判明する場合があるからです。


注釈:
Design:設計
Testing:テスト
Coding:コーディング
Design is difficult to test:テストによる検証が困難な設計

このループを最小限に縮める時も、上記と同じテクニックを使います。プロセスの順序を逆にして、設計の前にテストファーストプログラミングを実行します。つまり、テスト、コーディング、プログラム設計を全て並行して進めます。テストはコーディングに影響を与え、そこから設計にも影響が及び、その結果が次のテストにまた影響します。


注釈
Test-driven development:テスト駆動開発
Design:設計
Testing:テスト
Coding:コーディング

この方式を試してすぐに気付いたことがあります。このサイクルは有機的に、設計上のアイデアを引き出します。また、仕様の中で必要な部分だけを実装するようになりました。実装の手法もどんどん進化しています。現在は本格的なリファクタリングステップを設計に組み込んでいるので、過剰性能にならずに適切な設計をしていると確信できます。こうしてようやく、目の前に与えられた要件を満たす、必要十分な設計と適切なコーディングが実現できるようになりました。

この手法をテスト駆動開発(TDD)といいます。TDDでは、原則とパターンのリファクタリングを絶えず実施して、テストファーストプログラミングと設計の検討を組み合わせます。TDDでは良い意味での副産物がさらに増えました。作りこむバグの数が減っただけでなく、機能の実現に役立たないコードを書くことがなくなりました。これは設計ミスの防止につながるので、チームの生産性がより一層向上します。設計ミスは後工程になればなるほど、修正のコストが増大します。

ビヘイビア駆動開発の次のステップ

ここまでで設計・コーディング・テストを1つのループで行うというお話をしてきました。ここで、要求分析に立ち戻ってみましょう。要求分析までに、「我々が何を構築すべきかについての理解」はできているはずです。繰り返しになりますが、私たちはループの最適化に関心があります。なぜならそこに時間がかかるからです。従来ですと、たくさんの機能のリストがあらかじめ用意され開発者へ渡されました。開発者は各フェーズごとに全機能を手がけないと、次のフェーズへ進めませんでした。この方法だと、結果的に、必要のない機能までも実装してしまう場合があります。またそれだけでなく、想定外だった新しい機能を実装しなければならないことに気付く場合もあるのです。また必要とわかっていた機能であっても、新たな側面を発見することもあるかも知れません。


注釈:
Analysis:要求分析
Testing:テスト
Coding:コーディング
Design:設計
Not what we need:必要ないもの

先程のループのテクニックを、要求分析にも適用できます。先程は、機能を実装しようとする前に、テスト駆動で機能を考えました。ここで強調しておきたいことがあります。開発者はこのようなサイクルを、時間単位、時には分単位で実行します。1サイクルに何日も何週間もかけないでしょう。


注釈:
Testing:テスト
Coding:コーディング
Design:設計
Analysis:要求分析

しばらくこのテクニックを継続的に適用してみれば、私たちは全ての機能を最小単位までブレイクダウンしがちであり、そしてそれを一つ一つ矛盾なく実装しようとする傾向にあることに気付かされるでしょう。そしてこのテクニックを使えば、それぞれの機能がお互いにどう影響しているのかをより理解でき、それによって自分たちが変化に対してより早く対応できるようになったと気付くはずです。さらには、不必要な機能の特定や、機能の優先順位づけも速くなるでしょう。

要求分析もテスト駆動で行うことで、私たちがより深く理解できることがあります。それは構築すべきシステムの振る舞い、そしてそれを適切に設計し実装する方法です。また同時に、開発一日目からテストスイートを作成していることになります。これにより私たちは、システム全体を常に検証することが可能なのです。


注釈:
Behavior-driven development:ビヘイビア駆動開発
Testing:テスト
Coding:コーディング
Design:設計
Analysis:要求分析

これが、ビヘイビア駆動開発と呼ばれる開発手法です。ステークホルダー(ビジネス・オーナー)にとっても開発チームにとっても、時間を節約できる手法です。開発者が早い段階で疑問点を提示することで、開発者自身だけでなくステークホルダーも、自分たちが構築しているものは何か、理解を深めやすくなります。ステークホルダーは、結果を得るペースを予測できるでしょう。さらに、各機能は小さなチャンク単位で実装されるので、見積もりはより正確になります。従って新しい機能も、計画や優先順位づけが容易になるでしょう。

さらなる高速化

ウォーターフォールモデル図にあるフェーズで、ここまでにまだ説明していないフェーズがあります。そのようなフェーズに着目すると、方法論は関係なく、同じように全フェーズを1つのフィードバックループにすることが、最小化なのではないかと疑問に思うかも知れません。答えはもちろん、イエスです。しかしそのようなループは、単なる設計や開発を超えたより広いスコープであり、全く異なるフィールドの人々を巻き込むでしょう。この記事のスコープ外の議論も出てくることになります。しかしながら、このようなループについても簡単に触れておきましょう。

非常に近いコンセプトとして、リーンスタートアップがあります。スタートアップとして築くべきものは何かを習得するループです。このループを確立させようと、要件、機能の開発、そしてマーケティングを一緒に行うのです。もちろん個々のプロセスは、企業によって多少異なります。しかしそれでも、様々なプロジェクトでリーンスタートアップの原則を適用することは同じでしょう。


注釈:
Continuous Delivery:継続的デリバリー
Testing:テスト
Coding:コーディング
Design:設計
Analysis:要求分析
Deployment:デプロイ
Maintenance:保守

BDDをデプロイや運用まで含んで行うとすると、継続的デリバリーのコンセプトが広がります。最も重要なプロセスは、継続的インテグレーション(CI)と継続的デプロイです。Semaphoreでは、どのようなプロジェクトでも簡単にこのモデルを適用できるようにしています。

結論

ビヘイビア駆動開発は、ソフトウェア開発プロセスにおいて、各フェーズを最適化させるところから、徐々に発展してきました。一つの小さなフィードバックループの中で、要求分析、テスト、コーディング、システム設計を行うことで、私たちは、ミスや不必要な作業を避けられます。これにより、よりよいソフトウェアを作ることができるのです。

TDDはテストのための手法であり、BDDもソフトウェア開発におけるテストのためだけの手法に過ぎない、と言われることがあります。これはよくある誤解です。いつもそうとは限りませんが、テストは、良い副産物に過ぎません。BBDとはソフトウェア開発の全体論的なアプローチであり、ある一つのシンプルな発想から生まれました。それは、開発作業の中で発生するフィードバックループを最適化したい、という発想なのです。

一般的なソフトウェア開発において、BDDの実践は、必須ではありません。また、BDDを習得し効果的に適用するまでには、時間もかかるでしょう。それでもBDDは、実践すれば持続可能な開発プロセスを結果としてもたらします。つまり、業務で利用できるソフトウェアの継続的な開発を可能にするのです。これには、十分な投資価値があると言えるでしょう。