読書

「レガシーソフトウェア改善ガイド」を読んだ

職場で規模大きめのリファクタリングを始めるかもしれないので、参考になりそうな本を読んでみることにした。

どんな本か

2016年11月10日発行。翔泳社。レガシーソフトウェアの改善および進化をテーマに、コンポーネントレベルのリファクタリングから大規模リライトまでの方式や注意点について書かれた本。著者はガーディアン紙のエンジニアChris Birchall氏。

https://www.shoeisha.co.jp/book/detail/9784798145143

所感

改善ガイドと銘打ってはいるものの大規模なリライトを積極的に推奨しているわけではなく、慎重なスタンスを取っていて現実的。時折頬を叩かれるような表現があり、大規模改修を前に冷静さを持つことの大事さを教えてもらった。

全10章のうちで特に参考になったのは3,5,6章。リファクタリングのメリットを組織や上司に理解してもらう必要性だったり、リファクタリングとリライトという大きな2択を取り扱っていて、言語やフレームワークに関わらず参考になると感じた。

その他の章はJava製Webアプリケーションを前提に書かれすぎているので、求めていたものと少し違っていた。私が目下直面しているのはRuby on Railsのレガシーソフトウェアなので、静的型付け言語のIDEやFindBugsは別世界の話だった。Jenkins、Ansible、Antなどのツール利用についてページを割きすぎていたのも少し的がずれている気がする。ただ、これらのツールすら整備されていない環境が本書の対象と考えると納得できなくもない。

要点

今後参考になると感じた内容をまとめておく。

レガシーソフトウェアあるある

厳密な定義ではないが、レガシーソフトウェアが主に持っている特徴。

  • 古い
  • 大きい
  • 引き継がれている
    • 作った人がいなくなっている
    • レガシーは遺産の意
  • ドキュメントが少ない
  • テストがない、またはテストできない

恐れとフラストレーションを乗り越える

  • 開発者の気持ち
    • 壊れやすいコードを直す事を怖がる
    • どこに何が書いてあるかわからず修正したくない
    • ドキュメントを書くことよりも更新し続けるのを嫌がる
  • 求められる動き方
    • 壊れやすいコードほど直すべき
    • どこでも良いのでコードリーディングする(調査的リファクタリング)
      • コメントを付ける
      • 不要な変数を消す
      • リファクタリングへの抵抗感が薄れていく
      • 複数人で行うことでチームのコード理解が深まる
    • 動的型付け言語はコンパイラの恩恵を受けられないので、自動テストにより重きを置く

レガシーコードの特徴

  • 失効コードが存在する
    • コメントアウトされたコード
    • デッドコード
    • 使われなくなった機能
    • 期間限定のコード
  • NULLを多用するコード
  • ミュータブルなオブジェクト
  • 複雑怪奇なビジネスロジック
  • ビューに入り込んだビジネスロジック

レガシーコードのテスト

テスト拡充とリファクタリング、どちらから始めるか問題と向き合う必要がある。場合によってはテストなしでリファクタリングをしなければいけない。

リファクタリングの前にユニットテストを書くことは時々無意味である。なぜなら、ユニットテストの対象範囲をリファクタリングで作り直してしまう可能性があるから。

一般に、あなたのリファクタリングによって影響されるであろうコードよりも、モジュール構成で1段高いレベルに、必ずテストが準備されるようにすべきだ。

リファクタリングの改善指標

ただ直すだけではどれくらい改善されたかがわからない。努力の結果が見えやすい方が良い。チームで改善指標を持っておくのが大事。様々なものが指標に使える可能性がある。

  • 静的解析の指摘件数
  • 性能、スロークエリなど
  • 内部エラーの回数
  • オペレーションの計時
    • 環境セットアップ
    • リリース、デプロイ
    • バグ修正の平均所要時間

どんなものが指標として使えそうか、チームでブレストしてみると良い。

リファクタリング対象の選定

ファイルの更新頻度の高さや機能としての使用頻度から導き出すと良い。更新頻度はGitを使っていればコマンドで抽出できる。

コードサイズが大きい、更新頻度が高い、機能としてもよく使われているものから直していく。

リファクタかリライトか

完全なリライト(書き直し)は多くの場合は良くないアイデアである。理由は以下のとおり。

  • 数年規模で長期化する可能性がある
  • 後から解決困難な課題が見つかるリスクがある
  • 現行仕様をそのまま移行することが困難
  • 現行システムのバグ修正を並行して取り込む必要がある
  • コード以外の足回りを整えるオーバヘッドが大きい
  • 完成しないと価値を生まない為、中断するとすべてが無駄になる

一方で、過去のしがらみに囚われずに実装できる点や、テスタビリティを高められるなどのメリットもある。

過去にリファクタリングに挑戦し、どうしても成功の道筋が見えなかった場合にのみ完全リライトを検討すると良い。

インクリメンタルなリライト

リファクタリングと完全なリライトの折衷案として、インクリメンタルなリライトという選択肢がある。

  • リライトを数多くの小さな段階に分割する
  • それぞれの段階でビジネス価値を提供する
  • 従って段階ごとに辞める選択肢も取れる
  • 論理的コンポーネントに分割し、ひとつずつリライトする

組織から了承を得ること

大規模なリファクタリングを片手間で行うのは困難。リファクタリングの必要性を会社に理解してもらったうえで、正式な業務として取り組むのが良い。

リファクタリングは、それがビジネスに長期的な価値をもたらすと証明できるときにだけ行うべきだ

正式な業務として大規模リファクタリングに取り組む場合は、プロジェクトとしての目標を設定する。目標はビジネスインパクトに関わる指標が望ましい。

見返りのあるリライト

リファクタリングもリライトも、工数に対してエンドユーザへの価値はとても少ない。時には交渉材料として新機能を提供する手段も取る。

3種類のリライト。

  • ブラックボックス的リライト
    • ユーザがまったく気付かないリライト
  • ブラッシュアップ的リライト
    • 外部仕様の変更やドキュメント拡充など、マイナーチェンジ
  • 見返りのあるリライト
    • 新機能追加
    • UIの刷新
    • 大幅な性能改善

開発者目線では最良の選択肢とは言えないが、組織に取り組みを承認してもらうには見返りのあるリライトが有効なケースが多い。

リライトにおける現行仕様の踏襲

よくある「仕様は現行踏襲」

  • 多くの場合、明確な現行仕様が存在しない
  • 謎仕様をそのままにするか、再定義するかを考える
  • 歴史を辿ってもわからないことはある
  • 既存のコードは尊重すべき。何らかのバグfixが入ってる場合が多い
  • 既存のコードはリファレンスとして扱う

リライト時のデータベース移行

データベースを移行するか既存とシェアするかは要検討事項。

シェアする選択肢

  • 移行や復旧の検討が不要
  • 切り替えのためのコードが不要
  • DBを選べない、アーキテクチャを変更できない(モノリスDB)
  • 新規用にスキーマ変更ができない
  • 新規アプリが不整合データを登録するかもしれない
  • 管理対象が一つであることのメリットを過小評価してはいけない
  • 新旧のデータモデルの変換層を設けることで対応できる

管理対象が一つであることのメリットを過小評価してはいけない

移行(新規作成)する選択肢

  • 現行との同期方式を検討する必要あり
    • リアルタイム同期
      • 既存DBのデータ更新の度に、新DBに書き込ませる
      • DBレイヤでトリガを使って同期するか、アプリを一度経由するか
    • バッチ式同期
      • 既存DBの更新分を一括で新DBに反映する処理、を定期実行させる
  • データ複製の監視ツールが必要
  • 新旧の両アプリケーションに同じリクエストを飛ばすのは危険なアンチパターン
  • リクエスト複製してもレスポンスは1カ所から返す
  • こういった同期/通知系の仕組みは、新システムではすぐに消せるようにしておく

リアーキテクティングとは

コードレベルよりもっと広い範囲のリファクタリングをリアーキテクティングと呼ぶ。モノリスなソフトウェアをモジュール化したり、マイクロサービスとして切り分けたりする。

リアーキテクティングによって得られるメリットは大きく分けて3つ。

  • モジュール化による品質
    • バグを生みにくくなる
  • 優れた設計による保守性
    • 関心が分離され、理解と変更が容易になる
  • 独立による自立性
    • チーム別に担当できる
    • モジュール別に言語選択も可

一方で、細かく分けられたモジュール管理の複雑化というデメリットも存在する。

アーキテクチャの選択肢

レガシーソフトウェアを改善する銀の弾丸はない。どのアーキテクチャを選択してもトレードオフだらけ。

  • モノリス
    • 言わずもがな
  • フロントエンド & バックエンド
    • 開発を分担できてスピードアップの可能性あり
    • FEとBEでサイロ化が進む恐れあり
    • 小さな変更でも時間がかかるようになる
  • SOA(サービスを細かく分ける)
    • 数十~数百のサービスを管理する必要がある
    • ログ収集の難しさ
    • サービス間のAPI連携管理が難しい
    • 認証系サービスなど、依存関係のホットスポットが生まれる
    • データベースの分離に立ち向かう必要あり
    • 各サービスで技術選択の自由が生まれる
  • マイクロサービス
    • 注意点はほぼSOAと同じ

生半可な気持ちでSOAやマイクロサービスに踏み切ってはだめ。「本当に必要なのか」を徹底的に考えてから、困難に挑戦するかどうかを決める。


わかってはいたことだけど、どんな場合にも通用する解決策は存在しないので、本書で得た知見を参考にして自分のケースではどう動くべきかを考える必要がある。

まだ大規模リファクタリングについて情報収集し切れていない気がしているので、次はマイクロサービスについての書物を読んでみようと思う。


<< 記事一覧へ