Symfonyアプリケーションの継続的なアップグレードの為にやっている事

[Symfony Advent Calendar](https://qiita.com/advent-calendar/2017/symfony) 19日目の記事です。

昨年のAdvent Calendarでは、Symfonyの長期運用PJやアップグレードに関する記事がいくつか登場しました。

- [3年育てたSymfonyプロジェクトの現状](http://tech.quartetcom.co.jp/2016/12/03/quartet-symfony-current/)
- [Symfonyをアップグレードする方法](http://www.karakaram.com/how-to-upgrade-symfony)

今年は、自分が管理しているSymfonyで作られたシステムの中で最古かつ、最も規模が大きい物が運用開始から3年経ち、更に先日2.8から3.3へのアップグレードにようやく漕ぎ着けました。そこで、今回はこのシステムで実戦している、Symfonyの継続的なアップグレードの戦略について書いていこうと思います。

## アプリについて

とあるアプリケーションのバックエンドで使っており、vendorを除いたPHPのNCLOCは約8万行（テスト含む）くらいの規模感です。
開発当初のSymfonyのバージョン2.4でしたが、2系のマイナーアップグレードを網羅して2.8で一旦留まり、先述の通り先日3.3に一気にアップグレードしました。

## Symfonyのマイナーアップグレードは安全

Symfonyユーザーの方には釈迦に説法ですが、おさらいしておくと、2.3以降のSymfonyでは[後方互換製の保証](http://symfony.com/doc/current/contributing/code/bc.html)が策定され、忠実にSemantic Versioning（以下semver）戦略に従っているので、一部の場合 [^1] を除いてマイナーバージョンへのアップグレード時にはアプリケーションが壊れない事を保証しているからです。これは安心ですね。

### マイナーバージョンアップグレードのメリット

一方で、Symfonyには[Long Term Support、通称LTS](http://symfony.com/doc/current/contributing/community/releases.html#long-term-support-versions)と言う長期間サポートされるマイナーリリースが存在します。マイナーバージョンへのアップグレードが後方互換性的に安全とは言え、パフォーマンスが低下する事例 [^2] もあるそうなので、堅牢でミッションクリティカルなシステム等であれば、LTSを渡り歩くのが妥当かと思います。
しかし、マイナーバージョンアップグレードにはいくつかのメリットがあります。1つは、新機能が試せる事です。Symfonyは都度魅力的な新機能が追加されますが、semverでは新機能はマイナーアップデートでのみ公開できる為、これらをいち早く製品に導入するにはマイナーアップデートが不可欠となります。また、既存機能が非推奨となるのもマイナーアップデート時の為、来るメジャーアップデートの時に備える事が可能となります。[^3]
当システムでは、これらのメリットを受けるためにマイナーアップグレードは常に行っていました。

因みに、マイナーアップグレードの手引は[こちら](https://symfony.com/doc/current/setup/upgrade_minor.html)が参考になります。

## メジャーアップグレードの難しさ

semverでは、メジャーアップデート時には後方互換性の破壊が認められます。殊Symfonyに関しては、先述の通りマイナーアップグレードを繰り返していれば、メジャーアップグレードの後方互換の破壊はあまり恐れる物ではありません。事実、Symfony 3.0では場合、2.x時代に非推奨となった機能が削除されただけで、その点だけを除けばマイナーアップデートとなんら変わりはありませんでした。この点は4.0でも踏襲されています。 [^4] （勿論今後はどうかは分かりません。1.x -> 2.0は全く別物になりましたし・・・）

しかし、Symfonyに依存しているライブラリを使っている場合、注意が必要です。特に、サードパーティのBundleが新しいSymfonyに追従しているかは非常に重要な問題で、事実、僕が使っていたJMSDiExtraBundleは、Symfony 3.xの互換性がある物の、3.2以降で使うと機能テストでDIのコンパイルキャッシュが効かなくなると言う重大な問題があった為、現在パッチを作って凌いでいます。（この件は[PR](https://github.com/schmittjoh/JMSDiExtraBundle/pull/285)を一応送っているが反応が無い）

余談ですが、AutoWiredのある現在でもこのBundleを使い続けている背景として、ジョブキューとして使っているJMSJobQueueBundleがこれに依存していると言うのがあり、今後の対応を鑑みて現在は自前で作った[ジョブキューのライブラリ](https://github.com/issei-m/simple-job-dev)に絶賛移行中です。移行のしやすさを考慮し、JMSと同様、RDB駆動（一応Redisでも動く）していますが、まだ絶賛開発中なのと今は一部の機能がプライベートリポジトリ上にあるので、これの宣伝はまたいつかさせて下さい。笑

## 今回3.3に飛び級した理由

冒頭でもお伝えした通り、このシステムでは2.8から3.3に一気に飛び級しました。理由は2つあって、1つは単純に3.xへのアップグレードを画策し始めたのが今年（2017年）からで、[ロードマップ](https://symfony.com/doc/current/contributing/community/releases.html)でも分かる通り、3.2以下は今年中にバグ修正サポートが止まり、年明けすぐの1月でEOLとなってしまうからであるのと、もう1つは今年の11月末にLTSである3.4が出るので、その直前のバージョンのアップグレードしておく事で、3.4への移行をスムーズにしようと思った為です。

### テストについて

2017年の今こんな事言うのもアレですが、テストは充実していたほうが良いです。後方互換生が保証されてるとは言え、パッチバージョンでもちょいちょい壊れます。 [^5]
2.xでは感覚的には、Formとそれに付随する、OptionsResolver、Validator、DI、Security辺りが結構壊れやすかったです。なので、これらをがっつり使っている場合はユニットテストを充実させたい所ですね。
DIに関してはコンパイル関連でこけたりする事が多かったような気がしましたが、基本的にほぼ全ての機能で通過する場所なのでまともな機能テストが1つでもあればすぐ検知できました。

とは言えFullStackなフレームワークのバージョンアップは影響度も大きいので、手動テストは結局は必要です。当システムでは、

- パッチバージョンアップの時はCIが通ればOK。それ以外は超大事な部分だけ手動でテスト
- マイナー（メジャー）バージョンアップの時は出来る限りのリソースを割いて網羅的に手動テスト

と言った感じで行っています。

## まとめ

- Symfonyのマイナーアップグレードは怖くない
    - と言うかこまめにしていたほうがメジャーアップデートの時楽なので可能な限りした方が良い
- サードパーティBundleを使う場合は、ちゃんとメンテされてる物を使おう
- テストは重要

## 来年について

[先日のksetaさん](https://qiita.com/kseta/items/5049043e4c8399a3c56b)同様、今年はインフラ周りに注力していてSymfonyの最新動向をあまり追えていませんでした。
しかしこれを言い訳にせず、来年はばっちりハックしていきたなと思います。まずはこのシステムのFlex化かなー。

　

[^1]: [セキュリティ脆弱性への対処](https://github.com/symfony/symfony/pull/24993)等やむを得ない事情の場合はパッチバージョンアップデートでも後方互換性が失われる事がある
[^2]: 最近PHP界隈をちょっとざわつかせた[Symfony 4: Performance out of the Box](https://medium.com/@fabpot/symfony-4-performance-out-of-the-box-eddc0782916f)によると、Symfony 3.4は2.x以降最遅
[^3]: UPGRADEガイドを見れば一気にアップグレードできなくもないけど、ぶっちゃけつらいのでDeprecation Notice見ながら少しずつ潰したい
[^4]: この為、.0がメンテナンス期間中であれば、メジャーアップグレードへの移行試験を簡単に行う事ができます。3.0の時は乗り遅れたので、4.0では是非試してみたいと思います。
[^5]: [^1] の通り