技術的負債を管理する
技術的負債は悪いものとみなされている。避けるべきものであり、できるだけ早く支払うべきものなのだ。
あなたもそう思うだろうか?私たちはそうは思わない。最初に技術的負債と財政的負債を比較して、戦略の設計とステークホルダーにおける類似点について驚くべき点があることを説明する。それからコード中の技術的負債を識別するための様々な可能性について一覧にする。それらはきっとあなたが対処しなければならないものだ。
最後にプロジェクトが技術的負債を返済するために取りうるいろんなやり方について述べる。あなたが返済したほうがよいのか、負債を変換するほうがよいのか、ただ注意を払うだけでよいのかを考える時にきっと役に立つだろう。
技術的負債について
開発者が新しい機能を実装するには2つのやり方がある。1つは素早くそして汚くやる方法で、これをやるときっと将来変更するのが困難になるだろう。もう1つは綺麗で頭のいい方法だ。実装するのにより長い時間はかかるが将来の変更は容易になる(マーティン・ファウラー氏のBlikiを参照してほしい)。だが同じ機能を綺麗に実装するためにプロジェクトのスポンサーがより多くのコストを支払わなければならないのは何故だろう?汚いやり方で実装すれば同じ機能をより少ないコストで提供できるのではないだろうか?テストカバレッジの収集を自動化するためにお金を費やさなければならないのは何故だろう?テストは機能ではないしビジネス価値を提供しないではないか!
汚いコードやテストの無いコードが顧客にとっては完璧に動いているとはいえ期待した通りのビジネス価値を提供しているなら、やがては極めて開発者に特化した制御不可能なコードベースとなり、最終的には柔軟性のないソフトウェアになってしまうだろう。汚いコードが山となれば開発部門はどこにもいけなくなってしまうだろう。
技術的負債という比喩 - 財政的負債との類似点と相違点
ウォード・カニンガムが技術者ではないステークホルダーにこの問題を伝えるために '技術的負債' という比喩を初めて使ったのは1992年のことだった。低品質で自動テストのないコードを財政的な負債と対比したのだ。ここで言うコードとは財政的な負債のようなもので、開発者だけではなく全てのステークホルダーが負うべきものだ。負債があるなら将来的に利子を払わなければならない。元本は将来の変更を容易にするためにコードベースを綺麗にリファクタリングするのにかかるコストだ。利子とは余計なコストのことだ。将来においてチームが綺麗なコードベースではなく汚いコードベースで仕事しなければならないような場合に支払わなければならないものだ。
財政的な負債と違って技術的な負債は返済しなくてもよい。時には返済が無意味なこともあるだろう。いくらかのコードはほとんどあるいはまったく読まれることも変更されることもないからだ。したがって技術的負債においてはそれが発生する可能性についても考慮しなければならないのだ。将来その汚いコードに手を付けなければならなくなるような事態が起きるのはどれくらいの確率だろうか、そしてどれくらいの頻度だろうか。技術的負債と財政的な負債との相違点は他にもある。最初にその負債を負った人が支払う必要がないということだ。コードベースを保守することになった別の開発者が支払うことになるのだ。
技術的な負債が財政的な負債とよく似ているのはそれが必ずしも悪いだけではないというところだ。返済方法にあてがあるなら家を買うために借金をするのは理にかなっている。災害に見舞われるなどの理由で支払い不能になることが分かっているならクレジットカードで高級品を買い漁るのも当然だ。ソフトウェアの技術的負債に関心を持つことには、技術的負債を支払うことのコストよりも早期のリリースと組織としての利益を上げることにおいて利点があるかもしれない。元本の利息が投資の利回りよりも低い場合には財政的に負債は良いことなのだ。同じことがソフトウェアにも当てはまる。市場に先じることよりも内部的な品質を犠牲にするとしても、それが報われるのは歩留まりが上がる場合だけなのだ。だがそれはリスクでもある。そういった利点には不確実性がつきものなので事前に推測するのが困難だからだ。
技術的負債と戦略的デザイン
エリック・エヴァンスの提唱する戦略的デザインという概念は私たちがどうやって技術的負債に取り組むべきかを教えてくれる。戦略的デザインでは、システム全体を通じて同じレベルの品質を保つことはできないと述べている。だからこそチームはシステムの部分によって品質に良し悪しができてしまうのを成り行き任せにするのか、積極低に統制していくのかを選ぶことができるのだ。ほとんど読まれることも無く触れられることも無く重要な要件を実装しているわけでもないコードは、完璧である必要は無いし、そのために多くの労力を費やすべきでもない。だからどの部分のコードの品質を上げるべきなのかが問題なのだ。特に求められていないのであれば品質は悪くないのに設計がダメな部分もあるだろう。この意見についての疑問はもっともなものだ。それぞれの部分のコードの品質が良くなくても構わないというなら、それは結局保守の不可能なシステムを作っているということになるのではないか。
戦略的デザインと技術的負債についての理解を深めることはプロジェクト内のコミュニケーションを促進するし、ひいてはプロジェクトのステークホルダーがより良い判断を下せるようになる。それがとても大変だというなら必ずしも全ての要件をエレガントに実装しなくてもよいことが開発者にも分かるだろう。手軽で雑なソリューションは最終的にプロジェクトが返済しなければならない負債となることが顧客にも分かるだろう。技術的負債は隠された品質の問題を含んでいる。ひとたび表沙汰になればプロジェクトを失敗させかねない氷山の一角なのだ。例えば多数のバグが出ている場合品質を改善するには費用対効果が問題になる可能性が高い。
技術的負債のステークホルダー
技術的負債はソフトウェアプロジェクトに関わるステークホルダーにとっての関心ごとだ。
- 顧客はバグや機能不足のせいで生産性が上がらないことに悩まされている。
- ヘルプデスクにかかるコストが増加するし、それもまた皆を悩ませている。
- 開発時間の増加と品質問題はマーケティングにおいても問題となる。
- バグ修正のための頻繁なパッチは運用チームの悩みのタネになる。
- イライラしてる人々がたくさんいるという事実は決して管理者を喜ばせることではない。バグや遅延やセキュリティの問題で悪評が立っている場合は特にそうだ。
- 開発者もまた苦しんでいる。誰も悪い成果を提供したいとは思っていない。さらに言えば誰も他人の為した悪い成果を引き継ぎたいとは思わない。
技術的負債は避けられない
ではなぜほとんどのプロジェクトは技術的負債を避けるために初めから正しいことをやらないのか?開発者は怠け者ではないだろうし関連技術やパターンの経験もあるだろう。技術的負債を強いるのはいつでも時間が足りないという圧力なのだ。開発者は短期的な生産性を高めたいがためにお手軽で汚い実装をして、目の前のリリースを低コストで済ませる。将来のリリースにおいて生産性は低くするしコストも高くつくことを分かっているのに。だがチームには目の前のリリースについてそれが本当に高い生産性と低コストにつながるのかどうかを知ることはできない。多分近道をしたところで思っていたよりもずっと早くにしっぺ返しをくらうだろうし、チームは考えいたよりも早い段階で利息を支払うはめになる。
だが私たち開発者は普通なら近道をしたいとは思わないし品質に妥協したくないものなのだ。時には外部からの制約によって速やかに終わらせてリリースに含めなければならないこともあるだろう。次のリリースが無いこともあるからだ。もしくはコードが読まれたり変更されたりすることがないことを前提としている場合もある。前に議論した "戦略的デザイン" では間違いなく品質について妥協することになる。そういう場合良いコードは "金メッキ" に比べてやり過ぎなのかもしれない。
ここまでに見てきたように、技術的負債という比喩は品質問題が手遅れになる前にすべてのステークホルダーへそのことを伝えるためのものなのだ。利子という比喩は、ダメなコード品質によって被る損害と品質が悪化していく様子をうまく捉えている。だがすべての技術的負債を返済しなくてもよいということはうまく表現できていない。だがチームにはどれくらいの負債があるのか、そしていつ支払うべきなのか正確には分からない。負債を押し付けられた人が必ずしも支払いをしなければならないわけではない。だが普通は誰かが支払っているのだ。現実って不思議だね!
技術的負債の識別
技術的負債の主要な問題はそれが明確ではないということだ。取引の明細を見れば誰でも負債額が分かる。だがどうすればチームが技術的負債を認識できるのだろうか?どんな指標を使えばいいのだろうか?
- チームメンバーのもたらした明細から匂いたつ一般的な "悪臭" 。"このコードを修正できるのはカールだけしかない"、"このコードをコピーしよう"、"私がコードを触ると全部台無しになってしまう"、あるいはコード中に残されたたくさんの TODO や FIXME コメント。(ニコ・ザズウォルカがうまく説明している)
- スクラムチームにはベロシティという尺度がある。チームは何も変更していないし外側の見た目も変わっていないのにベロシティが減少しているなら、そのソフトウェアシステムは大量の技術的負債を抱えているかもしれない。
- ソフトウェアの年齢。技術的負債の指標の一つとしてシステムが非常に古いライブラリを使っている、というものがある。もう保守されていないし新しいバージョンはずっと生産的なのに(例えば EJB2 と EJB3)最悪の場合だと古くなりすぎていてライブラリの開発者がもうサポートしていないことがある。
- 技術的負債は部分的には自動的に計測することができる。Sonar、SonarJ、Strucure101 のようなツールが正しく設定されていればコーディングプラクティスからの重大な違反を見つけられる。このブログ記事が示すように究極の解決策ではないが結構いいものだ。Sonar を使えば開発者がクラスやメソッドを適切なサイズに抑えつつサイクロマチック複雑度を低く抑えるような重要なコードメトリクスに従っていることを確認できる。Structure101 のようなツールがあれば構造的な問題を見つけられる。構造的な問題には循環依存も含まれる。2つの要素が互いに依存している場合、一方の要素の変更にはもう一方の要素への潜在的な影響がある。これら2つの要素は一緒に変更するしかない。もちろんそれらは分離されるべきだし独立して開発されるべきなのだが。Structure101 は複雑なコードを要素間の依存関係に基づいて見つけることもできる。他のクラスやパッケージへの依存関係があまりにも多いシステムは理解するのが本当に難しいし、もちろん保守も難しくなる。開発者が変更したい場合、たくさんのクラスやパッケージを理解してそれらの依存関係を考慮しなければならない。こういった問題はアーキテクチャに重要な課題があることを示している。
- コードカバレッジツールは実際どれくらいのコードが自動化テストによってカバーされているのかを検出することができる。このメトリックには一般的なガイドラインが存在しないので、注意して使わなければならない。一般的な指標としてカバレッジが90%以上であればテストケースが十分であると言える。逆に75%以下の場合深刻な問題があるかもしれないことを示している。
- システムが技術的負債を抱えているといってもそれにはいろんな状況があるし、コードから直接しることは出来ないのだ。出来の悪いソリューション、間違った技術の選択、ちゃんと動くんだけど良くない設計、ツールで検出できないくらい小さな悪意のあるハックなどが例として挙げられる。こういった状況において技術的負債はそれぞれ異なる方法で測定しなければならない。リリース毎のバグ数が急速に増加しているなら、ベロシティが継続して下がり続けているか、リリースが終わった後でもチームが極度のストレスに晒されているのかもしれない。
- 商用環境において問題が頻出しているというのはとても悪い兆候だ。このシステムが抱える問題があまりにも広範囲に渡るものなので、もはや信頼できるオペレーションが不可能となっている。
これらの指標はいずれも測定可能なものだが、コードから直接得られるものだけではない。これらの問題はそのうち解決されて忘れ去られていく。開発者が技術的負債のことを認識しているか、そのことを伝えたのに何も起きなかったとしても、遅かれ早かれビジネスの人々はそれが問題であると感じるようになるだろう。次の言葉は問題を的確に捉えている。「技術的負債は開発者が美しいコードを欲していることの言い訳ではないのだ。具体的なコストの要因でありプロジェクトのリスクとなり得るものなのだ」だからこそ技術的負債は目に見えるように、そして管理できるようにしなければならないのだ。現実の生活において借金すること自体は必ずしも悪いことではないが、意識して適切に扱わなければならない。ソフトウェア開発プロジェクトにおける技術的負債の支払いは純粋にビジネス上の判断なのだ。簡単に開発者に判断させていいものではない。
技術的負債を管理する方法
技術的負債は避ける事が出来ないことを理解しただろう。顧客側の技術者ではない管理職であっても、短期、中期、長期のそれぞれの観点で成功を収めるための最適なバランスを求めて技術的負債をできるだけうまく管理することに関心を持たなければならない。コード品質を改善するため、無駄にコードを綺麗にすることに時間を費やすのではなく有意義なビジネス上の判断をするにはどうすればいいだろうか?
「何もしないこと」とか「コードがどうしようもないのなら作りなおせばいい」とかの粗野な物言いは何の助けにもならない。
この記事では2つの有望なアプローチを検討する。いくらかのプロジェクトではすでにその有用性が確かめられているものだ。
- 技術的課題のバックログ化
- 要件の見積もりに技術的負債にかかるコストを含める
- その前にプロジェクトの特定の状況において意味がありそうな2つの重要なプロセスについて議論したい
- リファクタリングのためのバッファタスク
- リリースの後始末をする
技術的負債を議論するときに忘れてはならないのが、技術的負債は必ず返却しなくてはならないのかという問題だ。フランク・ブッシュマンは3つの戦略について解説している。これについては後で述べる。
- 負債の払い戻し
- 負債の変換
- 利息だけを払う
バッファタスク
リリースのたびにバッファタスクを1つ作る。例えばかかった時間の10%を割り当てる。チームメンバーはまだ予定されていないリファクタリングのためそのタスクにかかった時間を記録できる。その時間は将来起こるであろう未知の問題のために使われる。こういったバッファはスケジュールしやすいし使いやすい。だがそれによって不必要な仕事に時間をかけてしまうリスクもある。バッファの時間を有意義なリファクタリングに使っているかどうかは調べるものではない。開発者は単純にバッファタスクの時間を記録するだけだ。ほとんどの場合バッファの時間が最適に利用されることはない。本来ならビジネス上の判断であるにも関わらずどこをリファクタリングするのかを決める際は特にそうだ。残念ながらバッファタスクを使うということは何を為すべきなのかが明確に定義されていないということなのだ。
後始末リリース
あるチームでは時々純粋に技術的な理由からコードベースを改善するためのリリースをする。このアプローチは本当に必要なリファクタリングのリストがある場合にのみ有用なものだ。そうでなければ不要なリファクタリングで無駄に時間を使ってしまうリスクを負うことになる。このアプローチでは新しいフィーチャのリリースを遅らせることになるのでビジネスサイドのサポートは欠かせない。もちろんそのためにはビジネスの人々が技術的負債について理解していなければならない。コードベースを綺麗にするための純粋に技術的なリリースについて、そしてかなり労力のいるアーキテクチャの手直しについてあなたは考えるべきだ。開発をしているときも運用をしているときも、似通ったコード辺はいつも同じような問題の原因となるものだ。今となってはアーキテクチャが現在の要件に合っていないのかもしれない。こういった問題は小さなリファクタリングでは解決しない。後始末リリースなら大規模な変更ができるのだ。
非常に忙しく時間に追われながらリリースしたせいで大量の技術的負債を作りこんでしまった後に後始末リリースをするのでは何の意味も無い。新しいコードベースについての経験は微々たるものなのでどこを改善するべきなのか誰にも分からない。本来は改善すべきでないコードの変更は危険なことだ。
技術的課題のバックログ化
技術課題のバックログは純粋に技術的なワークパッケージを定義するための確立されたベストプラクティスだ。このタスクはタスク管理ツールや要件管理ツールで作成される。それぞれのタスクには技術的な変更に関する簡単な説明がある。プロジェクトにとってその変更が何故重要なのか。そしてどのコードを変更しなければならないのか、といった内容だ。他のタスクと同じように十分なソリューションを開発するためにどれくらいの時間が必要なのかを見積もらなければならない。加えてそのコードに特有の面白みが何なのか評価しなければならない。正確に見積もるのは難しいことだが判断材料にするだけなら "小" "中" "高" といった程度に大雑把な見積もりでも十分だろう。最終的には近い将来そのコードが読まれる、あるいは、変更される可能性がどれくらいになるのかも必要だ。
このアプローチには次のような利点がある。
- 技術的負債が誰の目にも明らかになる。工数の見積もりと将来的なコードの変更に与える影響の見積もりに基づくリファクタリングタスクが完了した場合にだけ全員の合意を得ることができる。
- タスクにかかる工数の追跡が簡単。
- 技術的なタスクとフィーチャのタスクは完全に分離されている。
- とはいえこのアプローチにも欠点がある。
- 顧客はバックログを決めてタスクの優先順位付けをしなければならない。顧客がそれをできるのは少しでも技術的な課題に関心がある場合だけであって、ソフトウェアに関する深い知識も無しに技術的なタスクの価値を決めることなどできない。
- さらに顧客には純粋な技術的タスクにおけるビジネス上の利益を理解できないかもしれない。ほとんどの場合、顧客が関係しているフィーチャとつながりの無い技術的なタスクが本当に必要なのかどうか不信感があるだろう。
顧客に決定できる唯一の特殊な状況として考えられるのはソフトウェアのアップデートと一緒に行うというものだ。時代遅れになったソフトウェアが問題を起こすということはほとんどの顧客にとって明らかなことだ。更新するべきだであると決定するかどうかは、更新にかかるコストやリスク、その必要性に依存している。Java ランタイムや O/R マッパーのようなコンポーネントの更新は、それが定期的に行われている限りではそれほど工数はかからないしリスクも小さい。Web フレームワークの置き換えや RDB から NoSQL への移行は非常に高くつくので、アプリケーションの性能やユーザーエクスペリエンスを確実に改善できるというビジネス上の根拠が欠かせない。こういった目的には技術的課題のバックログが適している。だがそういったタスクから見込まれる成果次第では別のプロジェクトを始めることもできるだろう。
新しい要件を実装しなければならないのにコードベースの設計があまり良くないものだとしたら技術的課題のバックログには向いていないだろう。そういう場合は要件を実装する前にリファクタリングが必要だ。そのためにも要件の見積もりにリファクタリングのための工数を含めなければならない。
技術的負債による工数を含めた要件の見積もり
戦略的デザインによればコードベースのあらゆる部分を非常に良い状態にするべきではなく、変更容易性がビジネス上の価値につながる部分や、他の理由から頻繁に変更されるであろう部分について良い状態にするべきなのだ。したがって稀に、あるいは、全く変更されない部分のコード品質への懸念は無視することができる。正当な理由が無ければ決してリファクタリングしてはならない、ということも重要だ。新たな要件が具体的なビジネス上の価値を提供するのだとしたら、ダメなコードベースに実装するのではなく事前にリファクタリングをするべきなのだ。そうすれば重要な要件を "十分に良い" コードベースに実装していくことができる。次回のリリースに含める重要なフィーチャの実装を担当するチームは、リファクタリングの時間を確保してから実装するべきだ。もちろん例外はあるがそれについては "支払うべきか支払わざるべきか" の節で議論する。
この手順では、不必要な改善作業が行われることを防ぎつつ、リファクタリングを要件と直接関連付けることができる。これにより顧客はリファクタリングの優先順位付けについてチームと議論できるようになる。また、負債と利子の上に要件を実装するべきかどうかを判断できるようになる。市場投入までの時間のようにそれ以外の考慮すべき要因もあるだろうが。さらには要件にもとづいて実装されたコードが将来的には変更されることが分かっているかもしれない。従って将来のコストを抑えるために綺麗なコードベースへリファクタリングするという決定をするかもしれない。
実装をしている間にチームはコードベースにまつわる問題に出くわすかもしれない。そうしたらチームは何をするべきか決めなければならない。約束した全ての機能の納期について打ち合わせをするかもしれない。その結果として被った技術的負債と利息の支払いは受け入れなければならない。もしくはダメなコードベースで行ったあらゆる実装を捨てることになるかもしれない。そこには本来あるべき締め切りへの圧力は無い。そうしたらチームはリリース期日を遅らせることになるリファクタリングの予定を立てなければならない。これはその時の状況や顧客の特定のニーズによってのみ決められる。
支払うべきか支払わざるべきか
少なくとも重要な部分のコードは綺麗になっていなければならない。本当に必要なコードだけがリファクタリングされるべきなのだ。だが特定のシナリオにおいてどのソリューションが適切なのか決めるのは難しい。フランク・ブッシュマンは3つの戦略を紹介している。
- 負債の払い戻し:リファクタリング、コードやフレームワークやプラットフォームのリプレースを技術的負債とみなす。
- 負債の変換: 今のソリューションを完璧なものではなく良いもので置き換える。新しいソリューションのほうが利息が少ない。完璧なソリューションが余りにも高くつくような場合には良い選択肢だろう。
- 利息だけを支払う:(ダメな)コードと共に生きていく。リファクタリングするほうがそんなに良くないコードで仕事するよりも高くつくからだ。
開発者も顧客もコスト、リスク、緊急性に基づいた決定をしなければならない。簡単に言うと、大規模なリファクタリングはどんなときでもビジネス上の決定によるものでなければならない。利息を支払うことについての重要な観点を忘れてはならない。ソフトウェアの賞味期限だ。やがてシステムが再開発されるあるいは廃止になるとしたらリファクタリングに価値はあるのか?
結論
技術的負債は抜け道のように思われている。チームの時間や手持ちのお金の節約にはなるが将来的なコストの増加につながるだろう。実際のソフトウェア開発プロジェクトにおいて技術的負債は避けられない。だが適切に処理されてさえいればそれは必ずしも悪いことではないのだ。
実際にやってみるのは難しい。ダメなコードの品質が将来の要件に与える影響を予測するのは難しいし不可能なことだからだ。だが技術的負債の全てを無視することでソフトウェアに深刻な影響を与えることになるのだ。チームと顧客が技術的負債のリスクを適切に扱っていればそれは管理できるのだ。私たちは技術的負債に対処するいろんなやり方を示してきた。いずれのやり方も特定の状況においてのみ使用されるべきだ。受け入れなければならない重要な事実を述べる。
- 技術的負債は常にそこにある
- 技術的負債は必ずしも悪ではない
- 技術的負債はどんな場合でも支払わなければならないわけではない
支払いはビジネス上の決定であるべきだ。支払額がごく僅かなものなら顧客と相談する意味はない。開発者は常に自分自身 返済は、顧客との協議は意味をなさないように小さくても、開発者はコードの改善が時間と労力に見合うものなのか常に自分自身に問いかけ続けなければならない。
著者について
スヴェン・ヨハンはアムステルダムのトライフォーク社に勤務しているソフトウェア開発者だ。スヴェンはペアプログラミングや TDD、小さなリリースといった XP のプラクティスの熱心なユーザだ。現在はスイスの学校で使うオンラインアセスメントシステムの開発と、オランダで使われている自社の QTI エンジンの開発をしている。
エバーハルト・ウォルフはドイツのアデスコ AG 社に勤務しているアーキテクト兼技術マネージャだ。彼はJava、Cloud、ソフトウェアアーキテクチャに興味を持っている。定期的に国際学会へ寄稿したり、いくつかの著作がある。