新版『テスト駆動開発』を読んで考えたことのメモ。
TDDとトップダウンな設計(ペア設計)の対立について
TDDと普段の自分の開発の様子とが対立するのか,はたまた対立せずに補完し合う関係なのか分かりかねている。
TDDでは,何か機能を作ろうというとき,まずテストを書く。こういう入力を与えたらこういう出力が返ってほしい,そのインターフェイスはこうであってほしい,という前提がアサーション等として記述されていき, それを満たすような実装が与えられていく。これは完全にボトムアップなアプローチで,全体像がどうあるべきといったトップダウンのアプローチではない。
さてその一方で,普段の自分の開発に目を向けてみる。自分が何かの機能を開発するときは「まずこういう登場人物がいそうだ,こういうクラスになりそうだ」というところまで事前におおまかに設計してから実装を始めることが多い。 チームとしてもそのようなやり方をしているように思う。
ここで問題になるのが,現行の「登場人物を考えてクラスにする」式の開発スタイルにナイーブにTDDを結合させても,上手くいかそう,ということだ もし仮に,ナイーブに二者を結合するならば,まずトップダウンに登場人物がクラスとして固定され,その挙動がTDDで開発されるというスタイルになるはずだ。 だがこのスタイルはTDDのメリットでもある「オーバーエンジニアリングを防ぐ」という側面を達成できない。 例えば本来不要であったポリモーフィズムが実装されていても,既にクラスが固定されてしまっている以上,コードを書ききるまでこの異常事態に気付くことができない。 ナイーブなやり方ではTDDは馴染まないように思える。
そもそもトップダウン的に「登場人物を考えてクラスとして固定する」を行っている背景として,自分のチームのペア文化がある。 複雑になりそうな機能は,ペアになりつつ「どのようなクラスが登場しそうか」とか「要件を満たせそうか」とか「うまくいきそうですね」,ということを確認してから実装にとりかかるというもので,おおむね上手くいっている文化だ。 この文化を使わない場合,僕のようなおっちょこちょいなエンジニアが(オーバーエンジニアリングといった)不適切な設計をしていても,それが実装されてレビューに出されるまでは気付けず,大きな手戻りを発生させることになるだろう。 (こうして書くと,我々のチームはトップダウンなアプローチが好きなのかもしれない) TDDに全部振るならば,こうしたデメリットを受けるのではないか?
さて,トップダウンの開発アプローチとTDDはどうやら馴染みが悪そうだという点が分かった。そして,ペア文化の前提には「不適切な設計を防ぐ」役割があり,TDDによってペア文化をパージするならばうまく設計が行われなくなるのではないかという心配があることがわかった。 しかしTDDを使うことで不適切な設計が防がれるとしたら?だったらあまり心配せずにTDDを実践してペア設計はそれほど積極的に行わなくても良いことになる*1。
TDD本では「自明ならば」実装をそのまま記述していた。先回りしてクラスを決めてしまうのも,それが「自明ならば」問題は無さそう。 一方クラスの設計が自明でないなら,そもそもクラスを先に考えるのは無駄ではなかろうか。
このことから,次のように考えた。ペア設計における「登場人物を考えてクラスにする」は指針や方向性の確認程度で強制力を持たず,詳細な設計は普通にTDDでやったら良いのではなかろうか。予想外のシチュエーションやより良い実装が見付かったなら,それを選べば良いし,その事で目くじらを立てる人もいないだろう。あくまでペア設計もTDDも「より良いソフトウェアのため」に実践するものなので,それが達成されれば厳格に作法を守らなくとも良いはずだ。
まだまだ設計にはわからないことがたくさんある。
ふりかえって
機能の作成にあたっては,まず登場人物(クラス)を考えて……こういう仕事をしてもらおう……これで動くかな……クラス図を書こう……という過程をさも当然のように受け入れていたわけだが,TDDによってその思い込みは崩れ去った。 先に必要な分だけのテストを書き,それを最低限に動作させ,リファクタリングに持ち込むというプロセスを試行することで,最終的にコードが出来上がっており,設計もできている,というのだから。 だが本当にそうなのか?という感覚があったのでこのメモを書くことにしたのであった。
*1:設計を伝授するという役割をもペア設計は担っている。一概に無くしても良いとは言えない