レガシーシステムを理解する方法

結構年季が入ったJavaのレガシーなwebアプリをRailsに移行するというプロジェクトを進めている。レガシーシステムは以下のような状態。

・オリジナルを作ったエンジニアは既に不在
ビジネスロジックに対するドキュメントは殆ど存在しない
単体テストは存在しない
・社内WAFで、ドキュメントなし
複数のシステムが連動している

このような状態は、年季の入ったシステムではわりとよくあることだと思うが、いかんせん「仕様がわからない」ということが色んな所でボトルネックになっていた。その対策方法について、メモしておく。

仕様を理解するための時間を確保する

「その内時間が経てば理解できるよ」という考えもあるだろうが、ある程度年季が入ったシステムをリプレースするなら、初期段階で多少コストをかけてでも、既存仕様を理解するための時間を作った方が効率的だと思う。既存仕様をよく理解しないまま移行開発を行っても、色々な抜け漏れが発生する。また、移行作業とは別に通常開発やテクニカルサポートも行うわけだが、既存システムへの理解が浅いままだと、毎回の調査時間が馬鹿にならない。チームの人数や仕様理解度にもよるが、初期段階で仕様理解の体制を整備してチーム内で共有する時間をとった方が、トータルでみた時間コストが少なくなるというケースは多そうだ。

レガシーコードといえば、「レガシーコード改善ガイド」が有名だが、この本にも以下のように書いてある。

多くの人は、自分にできる最も手近な方法でコードを理解しようとし、その他の方法を使いません。結局のところ、何かを理解しようとするために多くの時間を費やすのは、作業がはかどらず、正しくないやり方に思えるからです。理解する段階をさっさと終わらせることができたら、自分の稼ぎを得るための仕事に取りかかれるはずだと考えるのです。しかし、こうした考えは愚かではないでしょうか。

とはいえ、場当たり的に調査しても効率が悪い。レガシーコード改善ガイドには、レガシーコードを理解するための技法がいくつか解説されているが、自分はその中から以下の3つを好んで使っている。

・仕様化テスト
・ラフスケッチ
・試行リファクタリング

「仕様化テスト」でユーザーから見た仕様を理解する

ユーザーからみたアプリケーションの仕様(一番外側の仕様)を把握する際に効果的。レガシーコード改善ガイドによると、仕様化テストとは以下のようなテストをさす。

仕様化テストは、コードの実際の振る舞いを明らかにするテストです。「システムはこれをするべきだ」とか「こうしていると思う」ということを確認するテストではありません。仕様化テストは、システムの現在の振る舞いをそのまま文書化します。(略)実際のシステムの動きをそのまま文書化したものになります。システムの一部分の動作を理解できると、その知識と、システムに新しく期待する動作に関する知識とを使って、変更を行えるようになります。率直にいって、システムの実際の動作に関する知識はとても重要です。

このやり方の何がよいかというと、仕様理解とテストケース作成の両方が進むということ。(もちろん、テストを正式にやろうとなると、メンテナンスコストが発生するので、実際に運用にのせる際には、注意深く検討する必要がある。)

リプレースプロジェクトなら、単体テストではなく、E2Eテスト

自分のプロジェクトでは「ユーザーからみた仕様は殆ど変えずにリプレースする」という大前提がある。なので、既存のレガシーアプリに単体テストを書いても結局捨て去る事になり、殆ど意味がない。なのでE2Eの仕様化テストを書くようにしている。

ツールとしては、selenium ideが便利。見返しやすいし(slowモードで再生すれば、ああ、こういう動きねというのが分かる)、後々自動テストを整備する際の資産にもなる。もちろん、selenium ideはメンテナンスがちょっと。。というのがあればcapybaraなど使って書くのもあり。この辺は用途やチームの状況に合わせて好きなツールを選択したらよいと思う。


「ラフスケッチ」で主要テーブル・クラスを理解する

コードを読むだけでは混乱する場合、絵を描いたり、メモを取ったりすることが有効です。(略)これらのスケッチには、完全なUMLダイアグラムや、関数呼び出しを表現する特別な記法を使う必要はありません。

上記の通り、形式はラフでよい。テーブル・クラスについて理解を深める時に効果的と思う。ただ、全てのテーブル・クラスを網羅しようとすると大変なので、スケッチの対象は、ビジネス上最も重要なテーブルやクラスのみにする。

ER図もどき

テーブルの関連だけ書いたER図があると、何かと便利。エクセルだとマスター管理が微妙なので、google driveの図形描画を使って、主要なテーブルの関連を書いている(とはいえ、結局主要テーブル以外の関連も調べるケースがあるので、erdというrubyのgemでER図を作るのもあり)

クラス図もどき

ビジネス上非常に重要な役割を担っているクラスは、クラス構造のスケッチがあると便利。別にUMLで正確に書く必要はなくて、継承や集約をざっと書いて、主要なpublicメソッドだけ書いておくだけでも役に立つ。継承関係の矢印ひっぱったりするのが面倒なら、手書きで書いておくだけでもいい。

「試行リファクタリング」で細かいロジックを理解する

ロジックが複雑で、かつそのロジックを把握しておかないとまずい部分は、試行リファクタリングを行う。レガシーコード改善ガイドによると、試行リファクタリングとは以下のようなリファクタリングをさす

まず、バージョン管理システムからコードをチェックアウトしてください。テストを書く事は忘れましょう。メソッドの抽出でも、変数の移動でも、そのコードを理解しやすくするために、あらゆる方法を用いてリファクタリングしてください。ただし、そのコードは再びチェックインせずに破棄します。これは試行リファクタリング(scratch refactoring)と呼ばれる手法です

リファクタリングを自由に行って、徐々にロジックへの理解を深めて行く。深掘りするとどこまでもいけるので、理解すべきポイントを把握したらすぐに切り上げるようにする。終わったら別ブランチに保存しておくか、もしくは破棄する。(決して稼働中のアプリに影響を与えないようにする)

まとめ

上記の方針で少しずつレガシーコードの理解を進めていて、結構いい感じにワークしている。なお、Object-Oriented Reengineering Patternsという書籍はこのテーマについて突っ込んで書いているようなので、読んでいるところ。以下から無料のpdfがダウンロードできる
http://scg.unibe.ch/download/oorp/



レガシーコード改善ガイド (Object Oriented SELECTION)
マイケル・C・フェザーズ
翔泳社
売り上げランキング: 118,912