JUGEM PHP

レガシーPHPプロダクトのアプリケーションアーキテクチャ改善活動(第1回/全3回)

JUGEM PHP

ブログサービス JUGEM でサーバサイドエンジニアの川島慧です。社内では @barie という名前で活動しています。

JUGEMは13年間続いてきたブログサービスです。そのコードは長い時間の蓄積とともに老朽化していて、かつそれを見直すこともないまま続いてきました。そのため数多くの問題を抱えており、ちょっとした施策を実施するのにもかなりの工数がかかったり、リリースしてから大量のバグが発見されるような状況が慢性的に続いていました。

今回は私がそんな問題をどう捉え、どう考え、どう改善して行ったのか、その活動内容を整理して全3回の記事にまとめていきたいと思います。その中で同じような問題にぶつかっていらっしゃる方に少しでも参考になるようなものがあれば幸いです。

JUGEMというサービス

前述の通りJUGEMは2004年2月から13年間続いているブログサービスです。実現技術はほぼPHP(一部Perl)で、既存のものも内製のものも含めてフレームワークは使われておりません。

既存のアプリケーションアーキテクチャの問題点

ほとんどのロジックがグローバルスコープに書かれていました。最低限としてViewが分かれているくらいでそれ以外の仕組みらしいものはほとんど無い状態でした。そのため以下のような問題が発生していました。

  • 既存コードの使い回しはほぼできない
    • 新しい実装をするときはまた一から仕組みづくりが必要
  • PHPファイル間でお互いに影響を与えうる
    • あるファイルの修正が別のファイルに思わぬ影響を与えてデグレが発生しやすい
    • デグレを回避するには影響範囲の調査が必要だが、複雑な依存関係のため時間がかかる

リポジトリ内にPHPファイルは1000個ほどありますが、これらがお互いに深い依存関係のあるいわゆる密結合の状態で、ファイルは分かれていても結局全体で一つの複雑に絡まった毛糸玉のような状態になっていました。デグレを避けようとして影響範囲の調査を始めようとしても、毛糸を1本ずつ解いていくような途方も無い作業工数がかかっていました。

php_is_tangled_yarn.png

修正方針

密結合となってしまっている既存のロジックを分解し、再利用性が高くかつメンテナブルなパーツにしつつ、それらを構造化していって開発効率をもっと高めていきたいと考えました。要するにアーキテクチャ無き世界にアーキテクチャをもたらそうとしました。

とは言え、アプリケーションアーキテクチャの決定は一般的にはフレームワークによって解決する問題です。ならばいっそフレームワークを入れてしまえと考えていた時期もあったのですが、もちろん既存のコードをいきなりフレームワークの仕組みの上に乗せ替えられるわけもありません、大変な工数がかかります。だからと言って独自フレームワーク的に自分で仕組みを作ってしまうとそれは長期的にメンテナンスという面で大きなリスクを抱えてしまいます。

出した答えはコンポーネント単位でフレームワークの仕組みに乗せ替えていきながら、少しずつフレームワーク化していくことです。全てを一気にフレームワークの仕組みに乗せ変えるのは難しいですが、小さな単位で少しずつ乗せ替えていくなら日々の開発の過程でもある程度実現できますし、かつそれだけでも構造化した恩恵が受けられるので、その後の開発効率にも良い影響があります。

目指すフレームワークは2017年現在PHPフレームワーク界で圧倒的な支持を得ているLaravelを目指していくことにしました。現在のコードを小さなパーツに分解しながらLaravel上での役割に配置していく方針で修正していきます。

サービスを成長させるための施策を日々実施しなくてはならないので、リファクタリングのためだけの工数を確保することは難しいです。なので日々の開発の過程でその時出来る範囲だけ修正していくことになります。少しずつ修正して行ったとして、果たしていつになればフレームワーク化というゴールにたどり着けるできるのかという疑問は残りますが、結論を言ってしまえばゴールにたどり着けなくても別に良いと思っています。アーキテクチャ無き世界にアーキテクチャをもたらす具体的な指針を持つこと、そして目指す先は世に評価を受けている高品質なアーキテクチャであることがこの方針のキモだと考えています。

戦略

さてLaravel化という具体的な方針を抱えたものの、実はそれだけで今のコードの全てを整理できるわけはありません。ビジネスロジックはフレームワークがあろうとなかろうと自分たちで作っていくコードですし、コード量はむしろコチラのほうが多いです。なのでコチラをどう改善しておくのか考えておくことが必要です。

既存のほとんどが密結合になってしまっている現状から真っ先に思いつくことはロジックのパーツ化です。現在の巨大なロジックから意味のある小さな塊を見つけ出し、そこからクラスへ切り出していくことで少しずつ疎結合な状態を目指します。

ただしこの方向でリファクタリングを進めていくとすぐ壁にぶつかります。これらクラス上で、ある時アプリケーションのコンフィグにアクセスしたい時が出てきたら?ログ出力するためにロガーオブジェクトを取得したい時が出てきたら?

一般的なフレームワークではこれらに手軽にアクセスするために仕組みが用意されています。巨大なロジックから少しずつパーツへ切り出していけばいずれ整理されたコード環境が得られそうですが、そのためにはアプリケーション全体で保証されるような巨大な仕組みがあらかじめ必要なのです。

結局のところ既存のロジックから少しずつロジックを切り出していくような単純な方法だけで上手くいかず、アプリケーション全体で保証される仕組みの整備も並行して進めない限り実現できません。私はこの方針を「上と下から同時に攻める」と表現しています。

strategy.png

この2つの面でそれぞれどんな順序で改善をしていったかを残り2回分の記事にて具体的にご紹介していきたいと思います。

第2回の記事へ