レガシーなPHPを改善する為に(新入社員が勝手に)取り組んだ3つの事

[NIFTY AdventCalender 2016](http://qiita.com/advent-calendar/2016/nifty) の3日目の記事です。

2日目の記事は @ConHumiさんの「[オブジェクトストレージとNginx でブログを作る](http://qiita.com/ConHumi/items/f4bd9b20accccb228e9b)」でした。
4日目の記事は @ykokwさんで「[タイマー + スクリプト + mobile backend = プッシュ通知の定期配信](http://qiita.com/ykokw/items/2d0748c970fe8e3d9900)」です。

どうも @yaamaaguuu です。

AdventCalenderの季節がやって来ました。寒い日々が続いていますね。
振り返ると早いもので自分が[NIFTY](https://www.nifty.co.jp/recruit/)という会社に新卒で入社してから、7ヶ月も経過したということになります。

本日は**レガシーPHPを改善するために(新入社員が勝手に)取り組んだ3つのこと**を書かせていただきます。

# <i class="fa fa-gift"></i> 本記事の内容

* **レガシーPHPを改善するための、方針決定~改善までのプロセスの紹介**
* NIFTYの新人はこんな~~(に勝手な)~~ことを取り組ませてもらえる

上記の知見を広める目的で、記事を書いています。

# <i class="fa fa-rocket"></i> 事の発端

OJT(On Job Training)の一環でプロダクトの改善に取り組ませて頂く機会がありました。

出てきたのは大きめなレガシーPHP。

コードの重複を減らしたいというオーダーのもと、今回はコピペの大本となる箇所を改善し、分割集約されたコードをところへ流用できる状況を作るということを2ヶ月弱の間に取り組みました。

そのプロセスや取り組みが以下の内容となります。



# <i class="fa fa-sort"></i> 担当となったプロダクトの状況整理

* 古くから頑張ってきたコード
* コードに手をいれる頻度は高くなく、プロダクトとして安定稼働中
* 新規に環境を立てられるプロダクトでは無い
  * PHPのバージョンは...
  * 開発は共有開発環境サーバーを使って
* API形式のサーバー
  * だが認証があり、WEB画面を経由しないと叩けない
* 回帰テストはある
  * が、手動でチェック
  * テスト出来る環境は本番と開発両方
* 単体テストは無い

# <i class="fa fa-book"></i> レガシーPHPを改善するために参考にしたもの

兎にも角にも、まず「どのような方針で改善を進めていかなければならないか？」を決定する必要がありました。

その際に参考にさせていただいたのは、 @naoya@github さんが、2013年のYAPCで発表していた「モダンPerlリファクタリング」になります。

* [発表の動画](https://www.youtube.com/watch?v=ODDdGs_pHiU)
* [発表資料](https://speakerdeck.com/naoya/modanperlrihuakutaringu-number-yapcasia)

Perlとタイトルには書いてありますが、他の言語にも応用が可能なプレゼンテーション内容になっていました。

# <i class="fa fa-flag-o"></i> 基本方針

「モダンPerlリファクタリング」の資料に記載されている、基本方針を抜粋させて頂くと以下の通りとなっている。

* 実行環境とアプリケーションを分離する
* 分離したアプリケーションの一番大きなAPIを叩く

## <i class="fa fa-flag"></i> 現状の環境を振り返り

* 実行環境とアプリケーションを分離出来ているか?
  * 分離出来ていない
  * だが本番環境と開発環境が別れている
* 分離したアプリケーションの一番大きなAPIを叩く
  * 本番環境と開発環境で実行できる回帰テストが存在する
  * だが手動

## <i class="fa fa-lightbulb-o"></i> 取り組むべきだと思ったこと

1. せめて自分の手元から、開発環境に対する回帰テストは自動化
1. レガシーコードの改善方法の定石として、より粒度の細かいユニットテストを導入
1. ユニットテストを気軽に実行できる環境を作成する

# <i class="fa fa-star-o"></i> 取り組んだこと

1. Seleniumを用いた回帰テスト自動化
1. PHPUnitを用いた単体テストの導入
1. 単体テスト用環境としてDockerの導入

上記について詳しく説明してきます。

## <i class="fa fa-wrench"></i> Seleniumの導入

コードに手を加えること自体の頻度が低く、大掛かりな変更も頻繁なBugfixも無いため、既存の回帰テストが手動で確認する形となっていました。流石にチェックすべき項目が多く、いちいち手と目を酷使するのは嫌だったので、手動回帰テストをラップする目的で、seleniumuを利用して勝手に回帰テストを自動化してました。

自分のローカル開発環境の問題を諸々考慮した際に利用出来る!となったのが、golangと[go-selenium](https://github.com/sourcegraph/go-selenium)です。新入社員向けの技術研修でgolangが採用されていることもあり、生かさない手は無い！という感じでした。

ばしばしコードを書き換えても、自動回帰テストで問題が無いか？をチェックできるようになり、結果として作業効率を上げることができました。

## <i class="fa fa-wrench"></i> 単体テストの導入

回帰テストがあるとは言え、回帰テストが完璧だと信用しきるのは怖く、せめて「自分のコードはxxxなケースは想定しているコードだ」と言える状況を作ることを目指し、導入していきました。

既存のコードを読み解き、コードを切り出していく際に、切り出した箇所だけを関数にして、それに対するユニットテストを追加していきました。基本的なユニットテストの書き方は割愛し、以下の参考ページを御覧ください。

* [PHPUnitのドキュメント](https://phpunit.de/manual/current/ja/installation.html)
* [PHPUnitの使い方まとめ2016](http://qiita.com/t_ishida/items/694b2356ed23a8d89295)

またそれに加えて**テストのカバレッジなど出力させ続ける事が非常に楽しい**です。

切り出したコード分だけを対象にテストをしてカバレッジを取得をすると、カバレッジ90%前後を維持するのが容易かつ、現在の状況がグラフで可視化されるので、モチベーション維持のツールになる可能性があります。

\* ただし、PHPUnitのカバレッジ自体はそんなに賢くないとのことなので、要注意

## <i class="fa fa-wrench"></i> 単体テスト環境としてDockerの導入

状況整理で記述したとおり、新しい環境を立てられる状況ではなかったので、(この先のPHPのバージョンアップも見越して)切り出したコードの分だけのテスト実行環境を、コードと同じ様に切り出してあげるのが良いのではないか？と考えていました。

独立した環境を手に入れるには他にも手段はVagrantを使うとか、他の手段がいくつもあったと思います。では、なぜDockerを採用したか?と聞かれると、同期との技術勉強会の標準環境としてDockerが採用されており、「これがDockerを仕事でも使うタイミングだ!」と思ったからです。

**~~要は使ってみたさが溢れてしまっただけです。~~**

なんとなく採用したDockerの単体テスト環境は、しばらくローカル環境でのみの利用で、正直VMと対して変わらない利用方法でした。

しかし、しばらくして、コードがリポジトリにプッシュされるたびにチーム内Jenkinsでコンテナが立ち上がりテストを実行できるCIの様な環境にチームの先輩が昇華してくださり、Dockerを導入した価値がグッと上がりました。


# <i class="fa fa-paw"></i> レガシーPHP改善まとめ

コード改善の為に以下を用意すると、新入社員レベルのプログラミング力でも安心してレガシーなコード改善に取り組めます。

1. まず[モダンPerlリファクタリング](https://www.youtube.com/watch?v=ODDdGs_pHiU)を見る。
1. 次に現状を振り返って取り組むべきことを確定させる。

今回のやるべきことに対してやったこと場合は以下の通り

* 回帰テストを用意
    * 1コマンドで自動で実行されるようする
        * Seleniumの導入
* 単体テストを書く
    * 切り出した単体テストだけでカバレッジを図り続ける
    * モチベーションを高く維持
        * PHPUnitの導入
* 変更ごとにテストできるよう環境を用意する
    * Dockerの導入


以上を持って、**レガシーなPHPを改善する為に(新入社員が勝手に)取り組んだ3つの事**とさせていただきます。