NIFTY AdventCalender 2016 の3日目の記事です。
2日目の記事は @ConHumiさんの「オブジェクトストレージとNginx でブログを作る」でした。
4日目の記事は @ykokwさんで「タイマー + スクリプト + mobile backend = プッシュ通知の定期配信」です。
どうも @yaamaaguuu です。
AdventCalenderの季節がやって来ました。寒い日々が続いていますね。
振り返ると早いもので自分がNIFTYという会社に新卒で入社してから、7ヶ月も経過したということになります。
本日はレガシーPHPを改善するために(新入社員が勝手に)取り組んだ3つのことを書かせていただきます。
本記事の内容
- レガシーPHPを改善するための、方針決定~改善までのプロセスの紹介
- NIFTYの新人はこんな~~(に勝手な)~~ことを取り組ませてもらえる
上記の知見を広める目的で、記事を書いています。
事の発端
OJT(On Job Training)の一環でプロダクトの改善に取り組ませて頂く機会がありました。
出てきたのは大きめなレガシーPHP。
コードの重複を減らしたいというオーダーのもと、今回はコピペの大本となる箇所を改善し、分割集約されたコードをところへ流用できる状況を作るということを2ヶ月弱の間に取り組みました。
そのプロセスや取り組みが以下の内容となります。
担当となったプロダクトの状況整理
- 古くから頑張ってきたコード
- コードに手をいれる頻度は高くなく、プロダクトとして安定稼働中
- 新規に環境を立てられるプロダクトでは無い
- PHPのバージョンは...
- 開発は共有開発環境サーバーを使って
- API形式のサーバー
- だが認証があり、WEB画面を経由しないと叩けない
- 回帰テストはある
- が、手動でチェック
- テスト出来る環境は本番と開発両方
- 単体テストは無い
レガシーPHPを改善するために参考にしたもの
兎にも角にも、まず「どのような方針で改善を進めていかなければならないか?」を決定する必要がありました。
その際に参考にさせていただいたのは、 @naoya@github さんが、2013年のYAPCで発表していた「モダンPerlリファクタリング」になります。
Perlとタイトルには書いてありますが、他の言語にも応用が可能なプレゼンテーション内容になっていました。
基本方針
「モダンPerlリファクタリング」の資料に記載されている、基本方針を抜粋させて頂くと以下の通りとなっている。
- 実行環境とアプリケーションを分離する
- 分離したアプリケーションの一番大きなAPIを叩く
現状の環境を振り返り
- 実行環境とアプリケーションを分離出来ているか?
- 分離出来ていない
- だが本番環境と開発環境が別れている
- 分離したアプリケーションの一番大きなAPIを叩く
- 本番環境と開発環境で実行できる回帰テストが存在する
- だが手動
取り組むべきだと思ったこと
- せめて自分の手元から、開発環境に対する回帰テストは自動化
- レガシーコードの改善方法の定石として、より粒度の細かいユニットテストを導入
- ユニットテストを気軽に実行できる環境を作成する
取り組んだこと
- Seleniumを用いた回帰テスト自動化
- PHPUnitを用いた単体テストの導入
- 単体テスト用環境としてDockerの導入
上記について詳しく説明してきます。
Seleniumの導入
コードに手を加えること自体の頻度が低く、大掛かりな変更も頻繁なBugfixも無いため、既存の回帰テストが手動で確認する形となっていました。流石にチェックすべき項目が多く、いちいち手と目を酷使するのは嫌だったので、手動回帰テストをラップする目的で、seleniumuを利用して勝手に回帰テストを自動化してました。
自分のローカル開発環境の問題を諸々考慮した際に利用出来る!となったのが、golangとgo-seleniumです。新入社員向けの技術研修でgolangが採用されていることもあり、生かさない手は無い!という感じでした。
ばしばしコードを書き換えても、自動回帰テストで問題が無いか?をチェックできるようになり、結果として作業効率を上げることができました。
単体テストの導入
回帰テストがあるとは言え、回帰テストが完璧だと信用しきるのは怖く、せめて「自分のコードはxxxなケースは想定しているコードだ」と言える状況を作ることを目指し、導入していきました。
既存のコードを読み解き、コードを切り出していく際に、切り出した箇所だけを関数にして、それに対するユニットテストを追加していきました。基本的なユニットテストの書き方は割愛し、以下の参考ページを御覧ください。
またそれに加えてテストのカバレッジなど出力させ続ける事が非常に楽しいです。
切り出したコード分だけを対象にテストをしてカバレッジを取得をすると、カバレッジ90%前後を維持するのが容易かつ、現在の状況がグラフで可視化されるので、モチベーション維持のツールになる可能性があります。
* ただし、PHPUnitのカバレッジ自体はそんなに賢くないとのことなので、要注意
単体テスト環境としてDockerの導入
状況整理で記述したとおり、新しい環境を立てられる状況ではなかったので、(この先のPHPのバージョンアップも見越して)切り出したコードの分だけのテスト実行環境を、コードと同じ様に切り出してあげるのが良いのではないか?と考えていました。
独立した環境を手に入れるには他にも手段はVagrantを使うとか、他の手段がいくつもあったと思います。では、なぜDockerを採用したか?と聞かれると、同期との技術勉強会の標準環境としてDockerが採用されており、「これがDockerを仕事でも使うタイミングだ!」と思ったからです。
要は使ってみたさが溢れてしまっただけです。
なんとなく採用したDockerの単体テスト環境は、しばらくローカル環境でのみの利用で、正直VMと対して変わらない利用方法でした。
しかし、しばらくして、コードがリポジトリにプッシュされるたびにチーム内Jenkinsでコンテナが立ち上がりテストを実行できるCIの様な環境にチームの先輩が昇華してくださり、Dockerを導入した価値がグッと上がりました。
レガシーPHP改善まとめ
コード改善の為に以下を用意すると、新入社員レベルのプログラミング力でも安心してレガシーなコード改善に取り組めます。
- まずモダンPerlリファクタリングを見る。
- 次に現状を振り返って取り組むべきことを確定させる。
今回のやるべきことに対してやったこと場合は以下の通り
- 回帰テストを用意
- 1コマンドで自動で実行されるようする
- Seleniumの導入
- 1コマンドで自動で実行されるようする
- 単体テストを書く
- 切り出した単体テストだけでカバレッジを図り続ける
- モチベーションを高く維持
- PHPUnitの導入
- 変更ごとにテストできるよう環境を用意する
- Dockerの導入
以上を持って、レガシーなPHPを改善する為に(新入社員が勝手に)取り組んだ3つの事とさせていただきます。