この文章の目的
開発者とステークホルダーが「技術的負債」という言葉で正しくコミュニケーションをとれるようになることをゴールとする。技術的負債については色々な所で語られるが、実際の現場では技術的負債が管理されてない事が多いのでは無いだろうか。この場で技術的負債という言葉についての知見をまとめ、たたき台とする事で、ゴールに到達する第一歩としたい。
対象読者
- 開発者
- 責任者/見積もりに対して決定権を持つ人
技術的負債とは何か
技術的負債とは、コード・設計の状態を表す見積もりのための言葉である。継続的に開発を行う上で理想状態から離れたものを負債という比喩で表していている。
たとえば、省略可能なプロセスを飛ばす事で開発の高速化を行う事があり、初期開発を高速に行う開発者の中には意識的・無意識的問わずこれを行っている事例が多々存在する。このようにして抱えられた技術的負債は長期的に見た場合に問題を引き起こす事がある。
技術的負債にはどういうものがあるのか
- 低いカバレッジもしくは全く書かれないテストコード
- 開発者・関係者の間でスムーズな共有が行われていない知見
- 無視されたコンパイラ警告・コード解析結果
- 不必要に複雑すぎる設計・コード
- コーディング規約に従わないコード
- 放置された嫌な臭い (リファクタリング対象にはよくこの比喩が使われる)
- 古すぎるバージョンの言語やフレームワーク
- 開発環境と本番環境の差異
など、コードの書き方からインフラ面や開発体制まで多岐にわたる。これらには計測可能なものと、そうではないものが存在する。「放置された嫌な臭い」のようなきわめて直感的・感覚的な要素もあるのが難しいところだ。
技術的負債がもたらす問題
障害が出る確率が増える
たとえば複雑性は開発者の理解を困難にしてしまう。いじってはいけない方法でコードをいじってしまうなどバグや仕様ミスを誘発する可能性は、技術的負債がたまればたまるほど増加する。
工期が余計にかかる
上述の通り、技術的負債による障害の可能性が増えれば、そうならないようにするためにコードの改修・追加にはより慎重にならざるを得なくなり余計な行程が必要となってしまう。
開発者のモチベーションの低下
技術的負債がたまればたまるほど、開発工程の中で不確実なものを確認しなおす作業が増えてしまう。これは開発者のモチベーションを大きく損なう可能性がある。
後続の技術者の技術レベル低下
技術的負債のたまった設計やコードを見て育つ技術者はそれの影響を受けることによってその人の持つ技術レベルが低下する可能性がある。特に新人育成などには良くないだろう。
放置し続けることで負債が雪だるま式に増える
技術的負債は、さらなる技術的負債を呼び込む。たとえば、中身をいじることが怖いという負債の悪影響によって、コピー&ペーストでコードを増やしたりして対処するというような事が生じてしまう。
技術的負債をどう扱うか
技術的負債はどういう経緯で抱えられたものかに関係なく、そこにある事を事実として技術者・ステークホルダーが共有すべき情報であり、管理されなければならない。工期・品質などプロジェクトに大きく影響を与えるからだ。
技術的負債に目を向ける
技術的負債を管理するには、技術的負債に目を向ける事が第一歩となる。
技術者が行うべき事
技術的負債を管理する為に技術者がすべきことはいくつかある。
技術的負債とは何か説明する
責任者が技術的負債を知らない・理解が乏しいのならば、まずは技術的負債についての説明が必要となる。
技術的負債の一例として説明に使いやすいのが、循環的複雑度だ。メソッドごとに制御構造の複雑性を計測するものだ。各種言語用に循環的複雑度を計測するプログラムは用意されていて算出された数字を元に説得をする事ができる。
- 1-10 シンプルに保たれていてリスクは少ない
- 11-20 リスク管理は可能な範囲である
- 21-50 複雑なモジュールになっていて高いリスクを持っている
- 51以上 不安定でとても高いリスクを持っている
なるべく早めに整えるべき環境
技術的負債には可視化できるものとそうでないものがあると述べたが、できる限りにおいて技術的負債を意味するパラメータは可視化されるべきだ。そこで特に重要なのは、CIとコード計測の仕組みを導入すること。ほかにも初期段階で導入しておけば技術的負債が増えにくくなるものはある。
- CI (継続的インテグレーション) の導入
- コード計測の導入
- コーディング規約の決定と、コードフォーマッタの導入
- git などのバージョン管理システムの導入
技術的負債を見積もる
コード計測は使いやすい説得材料ではあるが必ずしも万能では無い。また可視化不可能なタイプの技術的負債もプロジェクトには大きく影響を与える。そこで技術的負債を見積もる事が大切になる。
たとえば、技術的負債を抱えた状態で技術者がすべきこと - sandboxでは、スクラムチームでの技術的負債の見積もりの仕方が書かれている。
- プロジェクトを開始する前に、何を優先し、何を犠牲にするか、ステークホルダーに十分に認識を共有する
- 負債を定期的に観測し、ステークホルダーと共有できる単位で数値化し、ストーリーで語り、理解を得る
- 技術的負債における影響と、ポイントあたりの規模感を共通認識とし、閾値などを設けて仕組み化する
技術的負債を責任者に定期的に報告する
技術的負債をどう扱うべきなのか責任者に決定してもらう為に、可視化した・見積もった技術的負債について正直に責任者に伝える事が重要である。
責任者の選択
責任者は開発者が伝えてきた技術的負債についてどう扱うか選択しなければならない。
1つめ: 負債の返済を認める
技術的負債がもたらす問題と、負債の返済のコストを天秤にかけた時に、後者の方が軽いと判断できる場合には、この選択肢をとることになるだろう。これはその領域が負債の返済コストを支払ってもいいと言えるだけのビジネス上の価値があるという事でもある。
特に長期に渡って重要なプロジェクトであれば、こまめな負債の返済を心がけたい。なぜならば技術的負債はたまり続ければそれ自体が新しい技術的負債を呼び込むことになってしまうからだ。そうなる前にある程度でも負債の返済を心がければ、全体的に見て良い結果になる。
また、必ずしも全ての負債を返済しなければならないわけではない。返済コストに応じて部分的に返済するという方法もある
2つめ: 負債を返済しないデメリットを認める
さきほどの天秤で、技術的負債がもたらす問題点の方が軽いと判断できる場合にはこの選択肢をとることになるだろう。これはその領域が負債の返済コストを支払うだけの価値が無いという事でもある。
3つめ: 負債を返済しないデメリットを開発者に押しつける
この邪悪な選択肢は一時的にビジネス的な勝利をおさめるかもしれないが推奨されない。それは技術者の意欲を大きく低下させて離職の危機を招くだけでは無く、工期のさらなる延期や、クォリティの低下・コンプライアンスの違反などの危険性もはらんでいるからだ。これには消極的な「そもそも技術的負債について理解しようとしない」というのも含まれる。
開発者が得てして技術的負債に関してナイーブになるのは三つ目の戦術(消極的なものを含む)が往々にして採用されてしまうことが多い為だ。これは最終的にはステークホルダー全員にとっての不幸になりやすい。
技術的負債と戦う
どうやって負債を返済するのか
必ずしも一気に技術的負債を返済する必要はない。できることから手を付ければよい。
- コードを追加する時に少しずつ綺麗にする (ボーイスカウトルール)
- チームの誰かを技術的返済要員にする
- 定期的に返済タイムをもうける
- 見積もりに合わせて閾値を超えたら返済する
などの返済方法もある。
負債返済手段
技術的負債を返済する、あるいはそもそも貯めにくくする為の手段をまとめる。
知見をスムーズに共有する
そのプロダクトに必要な知識(ドメイン知識)は、スムーズに共有されていなければ圧倒的な技術的負債になる。特に見えにくい要素だけに注意が必要だ。
ドメイン言語を確立する
同じ言葉を、皆がばらばらの呼び方をしていないだろうか?できる限りそれらを一つの言葉に統一すべきだ。もちろんこれは設計やソースコード内部にも言える。呼称する言葉をうまく英訳してソースコードの中で使いたい。
Wikiなどを活用する
誰もが共通で見られて編集が行える、統一した文書システムが望ましい。ドメイン知識を文書化していこう。
コミュニケーションを密にする
技術者とステークホルダーのコミュニケーションが足りなければ、的外れなモノを作ってしまいがちなのでコミュニケーションを充実にする必要がある。この時、スループットよりもレイテンシを重視すべきだ。
環境整備を行う
VCS(バージョン管理システム)を使う
バージョン管理されてないソースコードは履歴を追いかけられない。VCSを導入すればコード変更時に、コードをコメントで残す必要がなくなる。変更されたコードの履歴は全てVCSが覚えてくれている。
現状ではなるべくgitを採用すべきだ。たとえばsubversionに比べてマージが賢い、チーム開発に向いた機能がある、いざという時のソースコードの復活手段、リリースに向けて作業が簡略化できる、などの利点がある。またgithubなど、世界的に標準的なバージョン管理システムだからノウハウやツールが多く存在する。
CI(継続インテグレーション)を構築する
Jenkins のような CI 環境をセットアップすれば、ソースコードが更新されたら自動的にビルド・テスト・静的解析などが走るようになる。
開発する環境と本番環境の差異をなくす
開発する環境と本番環境の差異が多ければ多いほど、開発はしにくくなる。開発環境で再現できない問題が生じたりするからだ。なるべく開発環境と本番環境では差異をなくすべきだ。
個人環境を簡単にセットアップできるようにする
チームに新しく人が入ったり、実験用の環境を作りたくなった時に、簡単にセットアップできるようになっていれば開発がしやすくなる。最近は vagrant/docker/chef などのツールがよく使われている。
コード静的解析を行う
様々な言語向けにコードの解析を行うツールがあるのでそれらを活用すべきだ。
ライブラリ・フレームワークや言語のバージョンアップ
古いライブラリなどはセキュリティホールを持っていたり、ウェブ上や本に書かれている情報が通用しなくなることがある。また新しいバージョンではより労力の少なくて済む技法が使えるかもしれない。
- 最新バージョンが必ずしも良いとは限らず、バグや仕様変更などに引っかかる事もある
- バージョンが古すぎるとバージョンアップ自体が困難になることがある
これらの要素を考慮した上で適切なバージョンにすることを心がけたい。
テストを書く
ユニットテストやインテグレーションテストなど、自動テストは開発を楽にする。コードをいじっても大丈夫かどうかの指標となるからだ。また動的言語では、静的型付き言語に比べてテストの重要性が増す。これは型が安全性を保証してくれないからだ。
テストがそもそも存在していない環境では、レガシーコード改善ガイドを参考にしたい。
リファクタリングを行う
技術的負債を返済する上で最も多く取られるのがリファクタリングだ。リファクタリングとは外的振る舞い(機能・API)を変えずに中身だけ改善することを指す。ただしリファクタリングを行うためにはテストが存在していないと厳しいのでまずはテストを書こう。
- Javaならば新装版 リファクタリング 既存のコードを安全に改善するを参考にしたい
- Rubyならばリファクタリング:Rubyエディションを参考にしたい
コンパイラ警告や静的解析の結果に対応する
コンパイラ警告や静的解析(lintなど)の警告は、コードの怪しさを表すので対処する必要がある。
コーディング規約に合わせる
コーディング規約に合わせる方がバグは出にくいし、読みづらくなくなる。書式などはエディタやツールを使って自動整形できるようにしたい。ほかの規約もなるべくツールを活用して自動検出できるようにしたい。