この記事は 富士通クラウドテクノロジーズ Advent Calendar 2017 の23日目です。
昨日は@kzmakeさんの分離型のコンパクトなキーボードlet's split(レツプリ)を組み立てた話でした。
久しぶりにハンダゴテ出して工作したい気持ちになりますね!おっっっしゃれー
はじめに
今年も終わりますが大掃除はしましたか?
気持ちよく年を明けるためにも、今年の負債を整理して来年度のスタート切りたいですよね。
今年も終わりそうなので、今年中できていなかった負債の棚卸しするにために負債回収方法を調べました♪
年末にプロダクトの振り返りを考えている方は是非御覧ください!!
技術的負債の棚卸し
掃除するための準備は、まず整理から
技術的負債というと、とてもマイナスなイメージに聞こえますが、
開発を続ける中での理想状態との差分を揶揄する言葉です。
継続的に開発していると、多かれ少なかれ理想とギャップできますよねー。
負債ができる要因は、「使いなれていない流行りのライブラリを使った」
「完成させるため実装速度を優先した」「時が経ち、バージョンが古くなった」など様々です。
まずは、現状把握のためにも、負債を認識するところからはじめてはどうでしょうか?
step1. 負債を列挙する
コードや仕様書をみながら、思いつく限り負債を列挙し、
負債を大まかに負債の種類ごとに分類します。
よくありそうな負債の分類は、以下のような感じですかね。
- コードの負債
- 〇〇は、もっとスマートな実装方法で書き換えられた
- 〇〇と☓☓間で循環参照がある
- 〇〇メソッド、〇〇クラスが仕事をしすぎている
- 正常系、異常系のテストパターンが不足していた
- 突貫工事により、パッチ処理を書いたまま放置したまま
- 各レイヤーの責務が整理されておらず、スパゲッティ
- システム構成など設計の負債
- リクエストが集中してもオートスケールしない
- 〇〇が冗長構成になっていない
- バリデーションが誤っており、意図しないデータがDBに入ってしまった
- 技術環境の変化による負債
- 当時流行っていたライブラリより、イケているライブラリがでた
- メンテされていないフレームワーク、ライブラリを利用している
- 使用しているサードパーティなライブラリが使ってみたら、他のライブラリ等と相性が悪かった
- LTS対応のバージョンが上がった
- バージョンアップすべきライブラリがあった
- 推奨されるシンタックスが変わった
- 運用レベルの負債
- 引き継ぎ漏れ等により、ある処理・仕様の意図が分からない
- 資料の置き場が集約されておらず、どこにあるか分からない
- 一部仕様がわからず、バグ回収に多くの工数を費やした
- 手動メンテ手順が多く、オペミスが発生したことがある
step2. リスクを考える
負債を列挙し終わり課題感が見えてきたら、
リスク内容/度合いをつけると優先順位が可視化され、
対応検討しやすくなります。
イメージとしては以下のような感じです。
負債内容 | リスク内容 | リスク度合い |
---|---|---|
〇〇は、もっとスマートな実装方法で書き換えられた | 冗長でコードが読みづらい | 低 |
〇〇と☓☓間で循環参照がある | メモリリークを引き起こす可能性,requireしたモジュールのメソッドやプロパティがundefinedになり意図しない挙動を引き起こす | 高 |
… | … | … |
負債回収の棚卸しした項目は全部回収する必要があるとは限らないので、
「やりたいことに必要な時間 > 利用可能時間」の場合には開発メンバー・責任者と相談し、
許容できる水準を話し合い、どこまでやるか決めましょう。
また、負債のリスクを分類し、優先度合いを可視化しておくことで、
事前にリスクヘッジしやすいので少しはマシな説明ができるはず...(そう信じたい)
before:「〇〇の仕様変更があったため遅れました、てへぺろ」
「連携サービスの〇〇の変更により、本サービスでバグが流出しました、てへぺろ」
after: 「〇〇の仕様変更があると☓☓に影響あるかもしれないので、遅延リスクが発生します、キリッ」
「連携サービスの〇〇の変更は、重大リスクにあげた☓☓に影響あるので対応の可否を確認します、キリッ」
頑固な汚れ(計画的に掃除が必要なもの)への対処法
負債の棚卸しが完了し、優先順位がみえてきても、
簡単に回収できない負債もあるかもしれません。
例えば、「〇〇と☓☓間で循環参照がある」は設計段階から見直す必要があり、
大きなリファクタリングが必要になるかもしれません。
大きなリファクタを伴う、そんな頑固な汚れは計画的に対応しなければいけません。
step1. 大きなリファクタが必要か判断する
クラス図を作成し、各レイヤーの依存関係を確認します。
リファクタしたい対象と変更した場合の影響範囲を考察します。
また、大きなリファクタをするという判断が必要な場合はメンバーの合意が必要です。
大きなリファクタリングは、すべてのプログラミングチーム間で、
あるレベルの合意が必要になります。※1
step2. 大きなリファクタ時の戦術を知る
リファクタリング名 | リファクタリング対象 | 対応策 |
---|---|---|
継承の切り離し | 1つの継承階層で2つの仕事をまとめて行っている | 2つの継承階層を作成し、片階層は"機能"にもう片階層に"実装"に分離する=Bridgeパターンの適用 |
手続き的な設計からオブジェクトへの変換 | 手続き的な形式で書かれたコード | 手続き型で書かれている操作対象をオブジェクトに変更して、振舞いを分割してオブジェクトに移動する |
プレゼンテーションとドメインの分離 | 問題領域のロジックを含んでいるView、Controller | 問題領域のドメインを別のドメインに移す |
階層の抽出 | 仕事をし過ぎているクラスがある。少なくとも多くの条件分岐を持つ部分がある | クラス階層を作成し、1つ1つのサブクラスが特殊条件を表現するようにする |
上記は新装版 リファクタリング―既存のコードを安全に改善するの第12章大きなリファクタを参照にまとめたものです。
それ以外にも大規模な変更を徐々に進めていくときに、BranchByAbstractionというテクニックは便利そうです。
(残念ながら、使用したことが無い...)
BranchByAbstractionを軽く説明すると以下のような流れです。
つまりBranchByAbstractionは、抽象化レイヤーを使用することにより、
複数のモジュールを共存させ、欠陥のあるモジュールを移行させる手法です。
大きなリファクタ時の戦術は知っておくだけで、リファクタ手順を考える助けになるので損はないっすね。
step3. リファクタ後のゴールを定め、戦略(手順)を立てる
設計レイヤーを変更する場合、リファクタに時間がかかるため段階的にリファクタリングする。
例えば、ビジネスルールが変更する度に横断的に変更が発生し、変更に弱い設計を変更する大きなリファクタになったとする。
DDDを導入していくという話だが、やはり段階的な実装となる。
上記のように各レイヤーの依存関係を整理し、途中段階でどのような設計構造になるか、
図示して認識を合わせておくと良さそうだ。
step4. 手順の目処がついたら、テストから書く
リファクタリングを開始するとき、最初にすることは常に同じです。
対象となるコードについてきちんとテスト郡を作り上げること
テストを書いたら、あとは戦術を駆使しながら手順どおり実施する感じ。
小まめにお掃除するために
負債の棚卸しをして、気づくことは小さなものばかりが積み上がっていることです。
(逆に大きなリスクを伴う負債が、たくさんあったら問題ですね)
小さな負債が積み上がらないよう普段から良い習慣をつけるのが大事です。(←当たり前すぎる)
自分達のチームでは、以下を行う習慣になってます。
- eslintなどを入れて、シンタックスが揃うようにする
- prettierなどオートフォマッターを利用し、シンタックスを楽に揃える
- 必ずテスト実施されたもの以外、マージしない(CIツールで自動化)
- テスト等の実行環境の違いを吸収するため、docker化
- 定例会とは別に振り返りの時間をつくり、KPTを回す
- 手動でやらざるおえない場合を除いて、Ansible等を利用する
上記に加えて、簡単に実施できるリファクタリングカタログをメモする習慣もやっていきたいです。
(チーム内で知見共有できるし、一度調べたことを他の人が調べずにすむ、pr時に指摘せずにすむ等メリットがありそうです)
もっと良い習慣あるよ!コレ取り入れた方が良い!というご意見があれば、是非コメント下さい♪
小さな負債を貯めないような仕組みも大事ですが、
ボーイスカウト精神で見つけ次第回収するようにしましょう!(精神論バンザイ)
参考書籍
どの言語でも実践できるリファクタリングカタログは「新装版 リファクタリング―既存のコードを安全に改善する」※1にメチャクチャわかりやすく記載されています。
名著ですねー!
また、Oreiilyから出ているRefactoring JavaScriptのPDFがタダで公開されています。
ES2015構文を使ったリファクタリング、テストの書き方が解説されていて素敵です。
タダ is 最強!!
まとめ
技術的負債の棚卸しは、理想状態に近づくための足がかりです。
リファクタリングをしようか迷っていたら、まずは技術的負債の棚卸しから着手してみてはいかがでしょう。
明日は @ysaotome さんの「モダンな開発・運用環境を導入するために奮闘した(している)話 2nd」です。お楽しみに!