開発者がテスト駆動開発をすると、生産性が上がる理由
PDCAサイクルとは業種に関わらず、経営レベルから現場レベルまで適用可能な 継続的改善を推奨・推進するマネジメント手法である。 そのプロセスは下の図のように、「Plan」「Do」「Check」「Action」の四つのステップの実行サイクルから成る。
Plan(目標設定)→→→→→→→→Do(実施) ↑ ↓ Action(変更すべき点の特定)←Check(評価)
「Plan」と「Do」のステップのみから成る原始的なマネジメント手法と比較すると、 「Check」と「Action」が増えている。これにより、PDCAサイクルには以下の特長がある。
継続的改善と聞くと、なんとなく品質の向上や業務改善が目的に思えるかもしれないが、それは誤解。 PDCAサイクルは生産性の向上にも大きく貢献する。何故だろうか?
その前に、生産性の定義について考えてみよう。 稼働率は生産性と呼べるだろうか? 答えは、NO!だ。
現時点では納品できないもの、在庫の山を作って生産能力を無駄にしても、 生産性が良くなったことにはならない。ある工程だけの効率だけを高める(部分最適化) だけでは、作業のフローが崩れ、在庫が増えるばかりである。 生産性は頭打ちどころか、在庫の生産、在庫の管理コストなどによって、実際には低下してしまう。
重要なのは、必要とされるときに、必要とされるものを、必要なだけ作り出すことである。 つまり、生産には目標があり、それこそが生産性の評価基準であるべきである。
『生産性は目標の達成率によって評価計測される』
では、本題に移ろう。PDCAサイクルを導入することにより、何故生産性が増えるのか?
それは、誰にでも分かる、非常に単純な理由による。 しかし、これを認めるということは勇気が要ることかもしれない。
PDCAサイクルを導入することにより生産性が増えるのは、
「『Check→Action』のステップが存在することによって、 生産能力が『目標に沿わない行動』に『消費』されずに、 『目標に沿った行動』にだけ『活用』されるようになる」
ためである。
つまり、今まで見落とされてきた本質的でない作業(JITなどでは「無駄」と呼ばれる)が なくなるために、生産性が向上するのだ。
このようにPDCAサイクルとは、品質と生産性を同時に向上させることが可能なマネジメント手法なのである。
1950年代、品質管理の父といわれるW.エドワード・デミング(W. Edwards Deming)博士が 提案したデミングサイクル(Deming cycle)の発展系、PDCAサイクル。 登場から50年を経てもPDCAサイクルが滅びない理由はただひとつ。 それが過去に本当に役立ち、今もなお有用なものだからである。 そして、これからもその価値が増すことはあれ、失われることは無いだろう。
テスト駆動開発(これはXP全体を意味する概念ではないことに注意。 強いて言えば、XPのプラクティスの中でテストファースト関連のプラクティスだけを指す)は、 PDCAサイクルを明らかに実践している!
下の図は、テスト駆動開発をPDCAにあてはめたものである。 決して、苦労してあてはめたわけではないことを明記しておく。 各工程は、とても簡単に図の中に収まった。
Plan(テスト計画、テスト実装)→→→→→→→Do(テスト対象コードの実装) ↑ ↓ Action(修正箇所の特定、開発者の成長)←←Check(自動テストの実行)
このテスト駆動開発とPDCAサイクルとの類似と、 テスト駆動開発が品質と生産性を大幅に向上させるという宣伝は、 決して無関係ではない。
IT業界と他の業種を比較すると、特にPDCAサイクル中最も重要な「Check」工程である 「テストの実行」が、完全に自動化されていることが大きく評価できる点である。 (一度開発サーバに設定してしまえば、昼夜を問わず、定期的な自動実行さえ可能なのである!)
これにより、極めて短い時間でPDCAサイクルを繰り返し適用することが可能であり、 これが「品質」と「生産性」の大幅な向上に貢献することは疑うべくもない。
将来楽をするためと言って、本当に必要となる前にコードを書いてしまう開発者は多い。 確かに、Calculatorクラスにsummaryメソッドを書く必要があるならば、いずれ使うであろうaverageメソッドも 一緒に書いてしまったほうが、効率が良くなったように思える。 しかし、本当にそうだろうか?
将来楽をするためと言うが、その将来というのはいつなのだろうか? それは明日?明後日?1週間後?それとも、1ヵ月後? このようにして追加されたコードが書かれてから1週間以内に利用されるのは、 実のところ稀である。あるいは、そのコードが必要になったとしても、 期待される挙動は当時の開発者が考えていたものとは違うかもしれない。
恐怖!開発者の期待を裏切るコード!
それとは別に、コードの量が増えれば増えるほど、理解のために必要となる時間は増すという事実がある。 修正の前に確認すべき事実の数が増えるわけだから、本当に必要とされるコードを追加することは ますます困難になっていくだろう。予測に基づいて書かれたコードは、目的達成を妨害するのだ。
リファクタリングをするにしても、本来必要の無かった作業が必要となってしまう。 例えば、計算を実行するためのメソッドに接頭辞calcを付け、summaryからcalcSummaryへとメソッド名を変更する リファクタリングを行うとき、使われていないaverageも一緒にcalcAverageへとメソッド名を変更する 必要がある。でも、待てよ?averageって使われてないんだよね? だとしたら、なんて無駄な作業に時間を使ってるんだろう! (とりあえずこっそり誰も使っていないaverageメソッドを削除するリファクタリングを実施してみる。 やっぱり誰も気付いていないようだ。ふう、ようやくこれで楽が出来るってものさ!)
さて、落ち着いて考えてみよう。そもそも、どうしてこうなってしまったんだろうか。 全ての始まりは将来楽をするためという考え方だった。 でも予測に基づいてコードを書いてみても、実際には思ったほど楽なんて出来ていなかった。 それどころか、無駄で邪魔なコードを増やしている始末だ。 ということは……たぶん……きっと……その方針が間違っていたのだ。 将来楽をするためにコードを書いてはいけないのだ。
本当に必要となるまでコードを書かないと考えることで、あなたの生産性はもっと増えることだろう。
「Plan」には、テストの計画と、テストの実装が含まれている。 どのようなテストをするべきだろうかと考え、それをテストケースとして表現するという作業は、 「Do」に相当する実装コードを書く前に行われるべきだ。
XPの中ではテストファーストと呼ばれているこの方針は、しばしば旧来の手法と対立するものとして論じられる。 でもどちらかというと、テストファーストだけを論じるほうが楽しいので、そうすることにする。
テストファーストとは何だろうか?あなたの開発スタイルがテストファーストになっているかをチェックするには、 以下の質問に答えればいい。
共にYESなら、あなたはテストファーストしている。では、テスト駆動開発とは何だろうか? たぶん、下の図を見たほうが手っ取り早く理解できるだろう。
(成功)/ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄\ ↑ (失敗) ↓ テストデータの準備→テストコードを書く→テストする→→→→実装コードを書く | ↑ ↑ | | \ \________/ / \______________________________/
基本的に、実装コードを書く前に行われる一回目のテストは、テストコード自身のテストである。 テストコードが「テストが常に成功を返す」「反転した論理演算子」などのバグを持っていないことを テストするために、テストに「失敗」することを明示的に確認するステップは欠かせない。
あなたは、こう考えるかもしれない。「何故、テストコードを書くのを後回しにしてはいけないのだろうか?」
努力が好きな人への説明はこうだ。 「どう努力したとき評価されるか分かっていなければ、どう努力するべきか分からないじゃないか。 どう実装したときテストをパスするのか分かっていなければ、どう実装すべきか分からないことになる。 そんな状態の開発者は、努力していると言えるかな?」
楽することが好きな人への説明はこうだ。 「目視、手動のテストはとても時間がかかって面倒臭いだろう? でもテストコードを書くことで、面倒で時間のかかるテストは これからずっとコンピュータ様に代わりにやってもらえるんだ。 君は、簡単なテストコードを書くだけで、バグを減らし、 将来のテストを楽に済ませることができる。トータルでは楽ができるんだよ」
完璧主義者への説明はこうだ。 「もちろんあなたは大丈夫でしょうが、テストコードを書くのを後回しにすることを許したなら、 最後までテストコードを書かない開発者が出てきてしまうでしょう。 テストカバレッジは、常に100%のほうがいいと思いませんか?」
頭の固い管理者への説明はこうだ。 「50年の歴史を持つPDCAサイクルをご存知ですか? テスト駆動開発はこの実績あるサイクルを完全に踏襲していて、非常に理に適っているんです。 あらゆる業種で効果は実証済みなんですよ」
最後に、実践するのが好きな人への説明はこうだ。 「まずやってみなよ。理由はすぐに自分で分かるだろうから」
もっとも、実践するのが好きな人は、もうテスト駆動開発の利点に気付いているかもしれないけれど。
これは、明確なバグを無視して作業してはならない、ということである。
まず、『バグは納品までに必ず修正する必要がある』。 しかし明確なバグを発見しても、(あるいは、指摘されても、) デバッグは比較的面倒な作業であるから、後で直せばいいや、 という考えが開発者の頭を過ぎるだろう。 だが、それは危険な誘惑なのだ。
『人間は時間の経過と共に忘却する生き物である』ため 『バグは時間の経過と共に修正の困難性が増大する』。 いつかは修正しなくちゃいけないなら、早ければ早いほどいいのでは?
あんまりバグを放置しておくと、『バグを前提としたコードが書かれてしまう(製造業での「不良品に対する無駄な加工」に相当)』かもしれない。 残念なことに、『バグが前提となったコードは、時間をかけて書き直したり、捨てたりする必要がある(製造業での「手直し、廃棄される不良品」に相当)』。 こういうことに時間を費やすことは、生産能力が『目標に沿わない行動』に『消費』されている状況に他ならない。
あるいは、明確なバグを容認する姿勢を見せたばかりに、 『開発者がコードを信用できなくなる』かもしれない。 これは本当に酷い結果を生む。
「ふー。色々見てみたけど、やっぱりボクのコードにバグは無いみたいだ。 だとすると……たぶん○○○○の作ったこのライブラリが悪いんだ。 ……きっとそうに違いない。糞ッ!○○○○の奴め!」
『開発者がコードを信用できなくなる』ときは、『開発者が別の開発者を信用できなくなる』ときだ。 これを最悪と言わずに何と言うのだろう。 そうして、めでたくバグはどんどん増殖し、開発者の時間をますます食い潰していくのだ。
こういったあらゆる問題は、明確なバグを無視する文化に起因している。 オーケー!準備はいいかい?今が古臭い文化との決別の時だ。
テスト結果に失敗があるときには、まず、そのエラーを修正しよう。
人間は間違う生き物だ。人間は間違いを繰り返す生き物だ。なぜなら、人間は歴史から学べるはずのことを、学ぼうとしないからである。
歴史?それは違う。人間は経験から学べるはずのことさえ、学ぼうとはしない。 ほら、また同じバグだ。しかもそれは前のバージョンで修正したはずのバグじゃないか。 同じ間違いを何度繰り返したか、君は覚えてるだろうか?
デグレード(デグレ)やエンバグ、リグレッションと呼ばれて認識されるようになった (用語が統一されていないのは、IT業界の歴史の浅さを物語っている) この前のバージョンで修正したはずのバグが復活してしまうという問題は、 バージョン管理システムと呼ばれる種類のソフトウェアを使ってソースコードを管理することでそれなりに対策することができる。
だが、本当にそれだけで十分だろうか。 いつデグレが発生したのか、知ることが出来たら最高なのでは?
デグレについて、テスト駆動開発の一環としても対策することができる。
バグ報告があったときには、バグをテストするコードを書く。それだけでいい。
これからは、もしまた同じようなミスをしてしまっても「Check」に相当する 自動テストがデグレの発生を教えてくれるだろう。 いつも、いつか潰したはずのバグがゾンビのように復活するかもしれないと、 ビクビク怯えながらコーディングしなくても済むのだ。
テストコードは開発者のかわりにバグを覚えていてくれるから、 テストコードさえしっかり書けば、デグレのことを本当にすっかり忘れてしまっても 別にかまわないだろう。その分、もっと大切なこと (例えば、それは新しい機能のことかもしれない)を、覚えていられる。
「Action」に相当するステップ。たぶん、品質向上と開発者のスキル向上に 最も効果的なのが、このステップだろう。
プログラミングスキルを高めるために、これほど有用な情報は無いだろう。 このステップが存在すれば、開発者は短期間のうちに成長することが出来る。
ある実験では、優れた開発者は平均以下の開発者の10倍以上の生産性を持つという結果が出たという。 だから品質と生産性に与える劇的な影響については、わざわざ言うまでも無いだろう。
あらゆる開発者は、今以上に成長できる可能性を持っている。
「動いているコードに触るな!」という、IT業界の常識があります。
しかし、テスト駆動開発では、この常識は全くあてはまりません。 なぜなら、期待通りに動くことを保証するテストが既に書かれているからです。 コードの挙動が意図しないものになってしまうかもしれないという不安の9割以上は、 実装コードに先立って書かれるテストコードと手間も時間もかからない自動テストによって、 取り除かれていると考えていいでしょう。
このように前提が大きく違ってしまったとき、 今まで正しいとされてきた常識は、途端に意味をなさなくなります。 むしろ逆の行動が推奨されることになるのです。新しい常識はこうです。
「自動テストが用意されている場合には、リファクタリングせよ!」
リファクタリングとは、コードの振る舞いを変えずに、生産性を向上させるために ソースコードを改善することです。
使われていないメソッドの削除、不必要なコードの重複の除去、 意図が伝わらないコードの修正、安全性を脅かすコードの改良、 間違っていたモジュール/クラス設計の改善。
新しいコードを書かなくても、生産性を向上させるために出来る「Action」は、 たくさんあります。リファクタリングはその一つです。
(注意していただきたいのは、リファクタリングはテストコードが書かれていることが 前提となる作業だということです。 ですから、テストとリファクタリングを同時に導入しようとすると、 ほとんどの場合努力と注意が分散したことが原因で、導入に失敗します。 また、テストを導入せずにリファクタリングだけを導入することは、 リファクタリングの定義上、そもそも不可能です。 真剣に導入を検討する場合、必ずテスト→リファクタリングの順で導入を計画しましょう)
リファクタリングの必要性についてはまだ議論の分かれる点もあると言われていますが、 リファクタリングの対象となるコードが、開発者の経験に基づいて発見された 開発上のボトルネック(または、将来のボトルネック候補)であることは明らかです。
問題の除去に必要になるだろうと予想される時間と、ソースコードから漂ってくるヤバい臭いとを比較して、 実施したほうが良いと思えれば、リファクタリングしてしまうのが得策でしょう。
テスト駆動開発は品質と生産性を劇的に向上させる。 なぜなら、それはPDCAサイクルを、継続的改善サイクルを、 あらゆる業種で50年もの実績を積んできたマネジメント手法を、 テスト駆動開発というサイクルそのものが実践しているのからなのである。
さあ、テストしよう!