Happy Holidays🎄
本稿は、 Engineering Manager Advent Calendar 2023 の25日目の記事です。
はじめに
技術的負債は、1990年代にウォード・カニンガムが提唱した「負債のメタファ」をもとにした概念です*1 。
彼は、上司にリファクタリングの必要性を説明するために、金融系のたとえ話の中で「負債のメタファ」を用いました。
「負債のメタファ」は、ソフトウェア開発において新しい機能を作るだけではなく、開発とともに得られた知識や理解を用いてリファクタリングをしなければならないことを、負債の返済にたとえたものです。
知識や理解が少ないまま開発をする──借り入れをする──ことは、より早く市場にソフトウェアを提供できて、そこから学びを得られるという良い側面を持ちます。
しかし、負債はいつか返済しなければならないことはもちろん、完済までの間、利息の支払いもしなければならないという側面を併せ持ちます。
現在までに、技術的負債に関するいくつもの考え方が議論されてきました。
スケジュールや顧客を優先したエンジニアリング上のトレードオフ*2
や、ソフトウェアに蓄積されるクラフト(できの悪いもの)*3
など、表現は多様です。
技術的負債の定義は、それぞれの考え方によって異なりますが、いずれも「技術的負債は、返済が必要なものである」という点で一致しています。
技術的負債は、計画的に借り入れるべきものであり、返済計画も作らなければなりません。
技術的負債の返済計画
ところが、技術的負債の返済計画について、確立された方法はありません*4 。
そのため、「技術的負債を返済しなければならないが、何から返済すればよいかわからない」という状況が生まれます。
結果、技術的負債の返済が後回しにされ、負債が蓄積され続けてしまうということが起きかねません。
この要因の1つは、技術的負債が目に見えづらいものであり、分類や計測が難しいことにあると、筆者は考えています。
技術的負債を何らかの形で可視化することで、プロダクトのフィーチャーと同じように、優先順位をつけられる(計画を立てられる)はずです。
そこで、本稿では技術的負債に対して、優先順位をつける方法を考察してみます。
技術的負債の抽出
ソフトウェア開発おいて、技術的負債を作ってしまうことはよくあります。
たとえば、次のような例があります。
私たちが開発したプロダクトの導入を検討しているクライアントがいたとします。
そのクライアントは、プロダクトの導入条件として、いくつかの機能追加を要求していました。
私たちの事業戦略において、非常に重要なクライアントだったため、私たちはクライアントの要求を実現したいと考えていました。
しかし、クライアントの希望している納期が、通常かかる納期よりもずっと前でした。
納期に間に合わせるためには、スコープを縮小するか、人員を増やすか、などの選択肢を検討しなければなりません。
これらの選択肢から、ソフトウェアの保守性を犠牲にするという選択をしました。
ソフトウェアの保守性を納期とトレードオフすることで、意図的に負債を借り入れたことになりました。
この例に対して、意図せず借り入れたものは、技術的負債なのでしょうか。
私たちの日常生活において、「うっかり借り入れをしていた」ということが起きたらたいへんです。
トラブルに巻き込まれているかもしれません。
しかし、ソフトウェア開発では、意図せず──無鉄砲で無意識に──借り入れをしてしまうことがあると言います*5 。
たとえば、 設計について無関心なチームは、知らぬ間に負債を抱えてしまいます。
この場合、このチームは負債を抱えていることに気付かないまま、利息の支払いをすることになるでしょう。
もちろん、利息を支払っていることにも気付いてはいません。
このように、技術的負債には、意図せずに借り入れて認知されていないものもあるでしょう。
これらをすべて抽出しなければ、技術的負債の優先順位をつけることができません。
抽出アプローチによる違い
認知されていない技術的負債を抽出するためには、さまざまなアプローチが必要です。
では、どのようなアプローチが効果的なのでしょうか。
この問いについて手がかりを与えてくれるのが、 Zazworka らによる研究です。
彼らは、開発チームからアンケートなどで抽出した技術的負債(定性的)と、ソースコードから自動的に抽出した技術的負債(定量的)を比較することで、技術的負債を特定する方法を研究しました*6 。
その結果、それぞれのアプローチで抽出した技術的負債には、一致するもの/しないものがあるとわかりました。
要するに、抽出元によって異なる技術的負債が見つかったのです。
そして、彼らは以下の結論を導きました。
人によって認知している技術的負債が異なっているため、さまざまなメンバーからのインプットが必要
それぞれの抽出元のインプットを組み合わせるには、合意を取るより集約するのが効果的
ソースコードの分析により、設計や欠陥に関する技術的負債を見つけられる
抽出アプローチによる違いは、 技術的負債は抽出元ごとの集合があり、これらの集合に共通部分があると理解できます。
図: 技術的負債の集合
このことから、技術的負債を抽出するためには、1つのアプローチに頼るのではなく、多様なアプローチを組み合わせなければならないと考えられます。
アプローチにより、技術的負債の全体像を把握できて、優先順位を議論できるようになるでしょう。
技術的負債の具体化
抽出した技術的負債について、優先順位を議論すると人によって意見が分かれてしまいます。
それは、技術的負債という抽象のまま──異なる解釈──で議論をしていることが、要因の1つです。
まずは、技術的負債という概念を具体化をしていきましょう。
具体化において重要なのは、エンジニアだけではなく経営者までの組織全員が、技術的負債に対して「共通認識」を持つことです。
なぜなら、技術的負債が大きくなるとエンジニアリング組織だけではなく、ほか組織や経営の意思決定を左右するものになるからです。
技術的負債の共通認識はもちろん優先順位まで、経営も含めた組織内のあらゆる人に説明できることが理想になります。
技術的負債の会計表現
技術的負債の具体化について、「技術的負債を会計で表現する」こと、すなわち財務諸表で具体化するというアプローチがあります。
会計という組織の共通言語を用いるため、経営者と一緒に技術的負債に対する共通認識を作れるのが大きなメリットです。
このアプローチについて、 Akbarinasaji らは、技術的負債を貸借対照表に計上する試みをしました*7 。
「技術的負債を会計で表現する」という観点において非常に興味深いため、論文の内容をもとにその方法を紹介します。
なお、本論文では、技術的負債を「開発・保守運用の過程で発生するソフトウェア工学上の義務」と定義しているため、新しい機能開発なども負債として扱っている点はご留意ください。
貸借対照表に計上するため、まずは技術的負債を一般的な会計と同じく、流動負債と固定負債に分けます。
流動負債: 1年以内に支払い期限が到来する負債。次回の製品リリースまでに修正されるべきもの。
固定負債: 1年以内に支払いの義務が発生しない負債。開発への影響が小さいかゼロで、次のリリースに延期できるもの。
さらに、流動負債と固定負債を以下のように分類します。
負債種類
負債名
定義
例
流動負債
TD Accounts Payable (TD買掛金)
リリースされたプロダクトに含まれる、顧客に対する責任を伴う負債
リリース後に発見されたセキュリティ脆弱性、顧客からのフィードバックにもとづく改善要求
流動負債
Short Term TD (短期TD)
期間内に解決が必要だが、現在リリースには直接影響しない負債
次リリースのブロッカーになっているバグ、リリースした機能のリファクタリング
流動負債/固定負債
TD Notes Payable (TD約束手形)
将来の機能強化や機能追加に対するコミットメント/義務
中長期的な計画にもとづく開発のコミットメント、利用技術のEOLに伴う対応
流動負債/固定負債
Deferred TD (繰延TD)
未納品・未リリースのソフトウェアに対する前払金(顧客に負っている義務)
先払いがされいるがまだ提供されていないサービスや機能
流動負債/固定負債
TD Provision (TD引当金)
将来発生する可能性のあるTDに対する見積もり金額
具体的にはなっていないが、発生する可能性があるTD
固定負債
Long Term TD (長期TD)
長期的に解決が必要な負債
利用技術のアップグレード、システムのリアーキテクチャ
表: 技術的負債の分類(*7を参考に筆者が要約。例は筆者の解釈)
この分類を貸借対照表に当てはめると、次のようになります。
図: 貸借対照表に技術的負債を反映した例(*7を参考に筆者が作成)
以上が論文中で提案されている、技術的負債を貸借対照表に計上するための整理です。
会計で表現する効果と課題
この分類に技術的負債を仕分けすると、定性的に優先順位を付けられます。
TD 買掛金および短期 TD は、リリースまでに解決する必要があるため、優先順位が高いと考えられます。
TD 約束手形は、期日が明確にあり、期日が近いものほど優先順位が高くなります。
長期 TD をはじめとする固定負債は、 長期的に返済が必要とはいえ、優先順位を低くしても大きな影響がないかもしれません。
ここで重要なのは、技術的負債を一口に語らず、流動負債/固定負債およびその詳細に分けたことにあります。
これは、負債という概念のままでは、立場によって意見の違いが生まれてしまうからです。
図: 技術的負債に対する意見の違い
これに対して、技術的負債を流動負債/固定負債に分けることで、エンジニアとステークホルダーや経営者が「短期 TD
は次のリリースまでに返済しよう」というように、特定の負債について議論ができます。
とりわけ、固定負債は返済計画を立てなければ、返済が後回しになりがちなものでした。
負債の規模によっては、事業計画にも影響を与える可能性があるため、流動負債よりも長期的な視点で返済計画を立てなければなりません。
技術的負債の分類により、固定負債の返済と事業計画の整合性を考慮しながら、返済計画を作れるようになるでしょう。
その方法として、たとえばテクノロジロードマップに、技術的負債の返済を組み込めます。
steam.place
しかしながら、技術的負債を会計で表現するというアプローチには、いくつかの課題があります。
その中でも、論文内で言及されている「技術的負債をすべて抽出して金額を見積もらなければならない」という課題が大きいと考えています。
また、技術的負債を見積もることは、人の見積もりによる精度のブレだけではなく、見積もりのためのコストもかかってしまうのです。
また、貸借対照表はある一定時点における負債を表すものですので、技術的負債を放置した場合の利息は表現できません。
そのため、「金利が低いならもっと借り入れたい」という意見に対して、どれくらいの利息がかかるのかは議論ができません。
技術的負債の定量化
定性だけでは技術的負債に優先順位をつけられないため、次は、技術的負債を定量化する方法を見ていきましょう。
技術的負債の定量化にもさまざまな試みがあります。
たとえば、クラウス・シュミットは、「機能追加時における、実際のシステムと理想的なシステムの工数の差」を技術的負債と考えました*8 。
この考え方は、技術的負債の内容にかからず技術的負債の定量化ができるため、技術的負債を非常にシンプルにしてくれます。
一方で、「理想的なシステム」は人それぞれ異なるため、主観の入る余地が多くなることは避けられません。
そこで、理想的なシステムに一歩踏み込んだ、 Nugroho らによる技術的負債の研究を取り上げてみましょう。
品質レベルによる定量化
Nugroho
らの論文では、技術的負債を「ソフトウェアの理想的な品質レベルを達成するために、品質問題を修復するためのコスト」と定義しました*9 。
「品質レベル」という明確な指標を設けることで、品質指標やソフトウェアメトリクスという客観的な数字で、技術的負債を測れるようにしたのです。
一般に 、ソフトウェアの品質が高いほどメインテナビリティが良くなり、技術的負債が少なくなると考えられます。
高品質なソースコードと低品質なソースコードを比較すると、どちらに技術的負債が蓄積しやすいかは明らかでしょう。
したがって、品質レベルを指標に用いることは、技術的負債の定量化において有効な方法と言えます。
では、品質レベルにはどのような指標を用いるのでしょうか。
論文では、 SIGのソフトウェア品質モデル を用いて、品質レベルを定めています。
このモデルは ISO/IEC 9126
(現在は ISO/IEC 25010 SQuaRE )にもとづいており、LoC やコードの重複、
McCabe
の循環的複雑度などを用いて、ソフトウェア品質を測定します。
これらのソフトウェアメトリクスを星1-5にレーティングして、指標としています。
<引用>論文で提示されているリスクプロファイルのマッピング例
この表では、ソフトウェアのボリュームを LoC で算出して、その中にどの程度のリスクがあるかの割合(相対的ボリューム)を出しています。
たとえば、 100,000LoC のうち 4,000LoC が McCabe の循環的複雑度が50以上であれば、とても高いリスクが 4% ある(星2の評価)と計算します。
ほかにも、依存関係の数や、重複したコードの割合などを用いて、最大のリスク値をもとにレーティングします。
このように品質レベルを測定したうえで、品質レベルを星2から星3へ──自分たちが理想とする品質レベルまで──上げるためコストが、技術的負債になるとしています。
技術的負債の見積もり
次に、品質レベルを上げるコストを見積もります。
これは、修復作業を行うために費やされる工数( RE:Repair Effort )を用います。そして、 RE が技術的負債の額面にもなります。
RE の算出には、再作業の割合( RF:Rework Fraction )と、再構築価値( RV:Rebuild Value )を用います。
RF(Rework Fraction): ソフトウェア品質を上げるために変更が必要なコード行の割合。
RV(Rebuild Value): システムの再構築に必要な工数(人月)。コード行数などをもとにしたシステムサイズ( SS: System
Size )とソース1行あたり人月( TF:
Technology Factor )を掛けて算出する。
この式では、 変更が必要なコード行数の割合に、コード行数あたりの人月をかけることで、技術的負債を表現しています。
また、組織によるリファクタリングの生産性(リファクタリングの経験、専用ツールの利用など)は、リファクタリング調整( RA:
Refactoring Adjustment )として組み込めます。
技術的負債の利息
Nugroho らは、技術的負債の定量化だけではなく、その利息の定量化まで試みました。
彼らは、利息を「品質が低い結果、ソフトウェアを維持するために費やされる余分なコスト」と定義しています[5]。
要するに、理想の品質レベルと実際のシステムにおける、メンテナンス工数( ME:Maintenance Effort )の差が利息であると考えたのです。
これを次の式で示しています。
MF(Main-tenance Fraction):
年間のメンテナンスによって変更、追加、削除されるコード行数の割合。システムに費やされる年間メンテナンス量を表す。過去のメンテナンス実績から算出可能(論文では、
SIG 実績データから15%を設定)。
QF(Quality Factor): 品質レベルの係数。品質レベルが高いほどメンテナンス費用は少ないという前提のもとに簡略化されたモデル。以下の式により星1-5にそれぞれ
0.5/0.7/1.0/1.4/2.0 の係数を与える。
時間の経過とともに必要になる利息については、 時間( t )と成長率( r )を用いて次の式で求めます。
ここでいう成長率は、 MF に含まれる技術的負債の割合を表しています。
技術的負債の定量化の例
ここまでに説明した定義をもとにして、技術的負債の定量化を行ってみましょう。
例として、2つのシステムを比較してみます。
1つは品質レベルが星1で、もう1つは星3です。
どちらもシステムサイズを136,500に設定して、ターゲットの品質レベルは星4とします。
リファクタリング調整( RA )は、主観が入ってしまうためここでは使用しません。
品質レベル星1のシステム
まず、品質レベル星1のシステムをみてみましょう。
表: 品質レベル星1のシステムの技術的負債
このシステムの場合、技術的負債が252人月あり、利息を36人月支払うことになります。
品質レベルが非常に低いため、技術的負債の額面が大きくなっています。
この状態のまま、3年後になると、技術的負債は291人月に増え、利息は42人月に増えてしまいます。
品質レベル星3のシステム
続いて、品質レベル星3のシステムをみてみましょう。
表: 品質レベル星3のシステムの技術的負債
このシステムの場合、技術的負債が65人月になり、利息は8人月になりました。
3年後でも負債の額面は抑えられているのがわかります。
このように、品質レベルを用いることで技術的負債が定量化できて、将来的な利息も算出できました。
品質で定量化する技術的負債の効果と課題
品質による技術的負債の定量化は、ソースコードから自動的に抽出して評価することで、技術的負債を客観的に整理できました。
そして、技術的負債がどれくらいの額面になるのか、また、技術的負債の利息がどれくらいかになるのかまで算出ができました。
利息の算出ができると、品質を上げるための投資対効果を見積もれます。
その一方、いくつかの課題もあります。
Nugroho らは、品質レベルの精度の低さ(丸められた星1-5の品質レベル)と、 リファクタリング調整( RA )が主観的になることを指摘しています。
そのほかにも、論文が発表された2011年と現在としては、ソフトウェア開発を取り巻く環境が大きく変わっていることを考慮しなければならない、と筆者は考えています。
現在までに、さまざまな開発言語やフレームワークの発展がありました。
さらに、Chat GPT や Copilot などの利用状況によっても、ソースコードから算出する工数は誤差が大きくなっています。
そのため、現在のシステムにおける RF と RV の算出は、少々を手を加えなければなりません。
まず、 RF についてのイメージをたしかにするため、論文で用いられていた、44件の Java のシステムから分析したという RF を例示します。
星1を星5に上げるためには、作り直しをするくらいの工数が必要になるということがわかります。
ターゲット/ソース
1-star
2-star
3-star
4-star
5-star
1-star
2-star
60%
3-star
100%
40%
4-star
135%
75%
35%
5-star
175%
115%
75%
40%
表: <引用>論文で提示されている RF の例.
しかし、これは2008年から2010年に集計されたデータを元にしています。
当時の Java 6 と現在の Java 、当時のシステム設計と現在の設計、さらにほかの開発言語とではまったく異なるものだと言っても過言ではありません。
そのため、例示されている RF をそのまま利用できないと考えられます。
これに対して、自社でソフトウェアメトリクスを集めることで、リスク割合をもとに RF を算出できますので、興味がある方は論文を参考にしてみてください。
また、 RV の算出においても1行あたりの工数( TF ) を算出する方法は、現在と事情が異なります。
論文では、バックファイアリング
という手法を用いて、ソースコードから工数を算出しています。
しかし、現在においては有用な手法ではなくなっているため、別の方法を考える必要があります。
簡易的には、 SLOC ( Source Lines of Code )を用いて RV を算出できますが、あいまいさをトレードオフしていることを理解して利用しなければなりません。
技術的負債の優先順位
ここまでに、技術的負債を会計で具体化する方法、品質レベルで定量化する方法を紹介しました。
最後に、優先順位をつける方法を考察して、本稿を締めたいと思います。
まず、技術的負債は定性的/定量的なアプローチを用いて抽出します。
たとえば、定性的な技術的負債は開発チームからのアンケートやインタビューによって、
定量的な技術的負債はソースコードの分析によって抽出しましょう。
抽出した技術的負債に優先順位をつけるためには、同じ物差しで比べられるようにしなければなりません。
1つの方法として、定性的な技術的負債を定量化をしてみましょう。
定性的な技術的負債の見積もりには、たとえば、前述した品質の定量化を利用できます。
図: 定性的な技術的負債の定量化(星3→星4)
品質レベルの定義はそのままですが、以下の点は調整しています。
TF: バックファイアリングの精度に問題があるため、 SLOC/月
を算出することで、一ヵ月あたりにアウトプットするソースコード行数を用いる*10 。
RF: 見積もり対象の技術的負債のみを見積もっているので、 RF はすべて (100%) を対象にする。
RV: SS * TF ではなく SS / SLOC で算出する。
そして、これらの技術的負債をリスト化してみます。
併せて、会計の分類も行うことで、貸借対照表へ反映できるようにします。
表: 技術的負債のリスト
さて、工数が算出できているということは、一般的な優先順位付け方法を用いることができます。
試しに、 RICE スコアを用いて優先順位をつけてみましょう。
なお、 RICE スコアにおける Effort は、RE - 利息 で算出することで、利息の返済効果を加味しています。
表: RICE スコアによる優先順位
以上の方法で、技術的負債をプロダクトのフィーチャーと同じように、返済の優先順位をつけることができました。
SLOC をもとにしているため精度は高くないですが、大まかな指針にはなるでしょう。
おわりに
本稿では、技術的負債を会計で具体化して、品質レベルで定量化することで、優先順位をつける方法を紹介しました。
これが活用できれば、技術的負債の返済計画も立てやすくなるでしょう。
しかしながら、課題も多くあります。
高い精度で定量化ができないこと、見積もりのコストもかかることにより、実用性は低いかもしれません。
また、ソースコードから抽出できない技術的負債は、人が認知していなければ補足すらできません。
図: 本稿の方法で捕捉できない技術的負債
この課題を解決には、さまざまな方法を検討しなければなりません。
ぜひ皆さんも、自分たちの組織に合った方法を考えてみてください。
併せて書籍もご覧ください
ソフトウェア会計をはじめとする、エンジニアリングマネージャーの知識について、『エンジニアのためのマネジメント入門
』という書籍でも説明しているので、ご覧いただけたらうれしい限りです。
参考文献