はじめに
- この記事では、PHPで長年運用されてきたWebサービスにおける経験談を語っていきます
- リファクタリングでシステムを安定化!日頃の取り組みや工夫を教えてください by カオナビ Advent Calendar 2022という、プレゼントカレンダー応募記事です
どんな状況だったのか
- 主に3点困るポイントがあったため、それらに手を打ちました
- 保守にあまり工数を割けておらず、動的・静的問わず、未使用ファイルや関数が残っていました
- (1) グローバルに配置されたPHPのサービスクラスファイルが複数アプリケーションから呼び出されており、アプリケーション側の実装が無くなってもメソッドが残り続けている状態
- (2) 数万に及ぶ画像やcss, jsファイルが、未使用と使用中のものが混在している状態
- 日々書いたコードは全てコードレビュー後にmergeされますが、そこに依存している状態でした
- (3) コード行数や循環的複雑度といった定量的な指標はなく、レビュワーの感覚に頼っている状態
その状況だとどう辛いのか
- (1) に関して
- 使っているか分からないメソッドや定数が大量に存在していました
- するとコードの可読性や保守性が著しく低下します
- エンジニアの仕事は"コードを書く時間より読む時間の方が長い"と言われるぐらいなので、当然業務効率が落ちている状態でした
- (2) に関して
- これも同様に、可読性や保守性が低下していました
- また、これらファイルがgit管理下のため、gitのリポジトリが肥大化し、デプロイ時間が増加するといった弊害も起きていました
- (3) に関して
- ユニットテストも無く、数百行に及ぶメソッドがいくつか存在していました
- 下手に手を付けると障害になりうる、誰も触りたがらないけど触らざるを得ないシステムが存在していました
そこにどう手を打ったのか
- それぞれ別のアプローチをしました
- 前提としてレガシーなPHP環境のため、モダンなツールはほぼ使えないものと考え、だいたいは自前でツール実装をしました
(1) PHP未使用メソッドや定数の削除ツール
- 結論、シェルスクリプトを自作しました
- スクリプト内容
- PHPファイル内のメソッドを自動的に洗い出します
- そのメソッドに対し、リポジトリ内でgrepをかけます
- phpファイルとymlファイルで使用されていない場合、それらメソッドが抽出されるため、それらを削除します
- これにより、数万行に及ぶ未使用メソッドや定数が削除できました
(2) 静的ファイルの削除ツール
- 画像やcss, jsファイルでも似たようなスクリプトを自作しました
- こちらはPHP製です
- スクリプト内容
- 静的ファイルを配置しているディレクトリから、ファイルを全て洗い出します
- それぞれのファイルに対し、リポジトリ内でgrepをかけます
- phpファイルで使用されていない場合、そのファイルを削除します
- この時厄介だったのは、ファイル名を動的に指定している場合です
- 例えばMVCのViewレイヤーにあたるファイル内で、for文による動的なファイル名指定をされているケースです
-
item{$i}.jpg
のような指定
-
- これらは正直検知が困難でした
- 結果としてそういった連番ファイルは一度削除してしまい、404エラーを返すログを見て復活…という手順を辿りました
- また、それらファイルは除外リストに加えて、次回以降は自動削除されない仕組みにしました
- ここは若干悩ましいポイントではあります
- 例えばMVCのViewレイヤーにあたるファイル内で、for文による動的なファイル名指定をされているケースです
- これにより、数千ファイルの削除ができました
(3) PHPで複雑なメソッド乱立問題
- 結論、PHPMDを使用しました
- レガシーなPHP環境においても導入可能だったためです
- また、"複雑なメソッド"の定義においては、循環的複雑度を測定しました
循環的複雑度とは
循環的複雑度(英: Cyclomatic complexity)とは、ソフトウェア測定法の一種である。Thomas McCabe が開発したもので、プログラムの複雑度を測るのに使われる。プログラムのソースコードから、線形的に独立した経路の数を直接数える。
引用:循環的複雑度- Wikipedia
- 分かりやすく言うと、そのメソッド内に存在するifやforといった制御文があるたびに+1カウントすることにより、メソッドごとの複雑度を定量的に測定する手法です
実際やったこと
- これを利用し、まずは循環的複雑度40以上のメソッドを洗い出しました
- 結果として数十個の複雑なメソッドが出てきました
- 最も数値が高かったメソッドは120を超えていました
- その後は循環的複雑度40を超えるメソッドを、1つずつ分割やリファクタリングしていきました
- 1ヶ月ほどで40以上のメソッドは0になり、その後基準を30に下げることで、より保守性を高めることができました
- また、これに関しては自動化が必須です
- なぜなら一度潰したところで、また循環的複雑度30を超えるメソッドが生まれてしまうためです
- というわけで、このPHPMDの処理をCI/CDに組み込みました
- これにより、循環的複雑度30超えのメソッドが再び生まれないように手を加えました
- 最後に、分割したメソッドにユニットテストを導入しました
- もともとテストが無い環境だったため、テストカバレッジは追っていません
- 追ったところで、1%未満をウロウロする未来しか見えないため
- ですが、手を付けるのも怖い状態からは大幅に改善したと言えるでしょう
- もともとテストが無い環境だったため、テストカバレッジは追っていません
最後に
- 今回は長年運用されてきたレガシーなWebサイトについて、リファクタリングで行ったことを紹介しました
- いかに手を入れづらい状況かはお察しいただけたでしょう
- 何より重要なのは、技術的負債は定期的に解消すべきである、ということです
- 変えるべきなのはソースコードよりも、"保守を軽視してきた文化そのもの"です
- 一度無法地帯と化してしまうと、それを元に戻すのには莫大なコストと時間がかかります
- あらゆる書籍で『2割は保守に充てる』といった話が出てきますので、それぐらい常に工数を確保して、リファクタリングをしていきましょう