こんにちは。テクノロジーG Laravelバージョンアップ隊長だった矢田です。
弊社システムで利用しているLaravelのバージョンアップを半年ほどかけて行いました。
その内容と振り返りをまとめてみたいと思います。
背景
フレームワーク・言語のバージョンアップは労力のわりに得られるものが大きくないのでなかなか腰が重く手を出しにくい課題かと思います。そのため、クラシコムでも随分古いバージョンのLaravelを運用していました。
調査などでWeb検索して新しいバージョンのLaravelの機能を見るとうらやましく思い、ずっと指をくわえて見ている状態でした。
しかし「このままではいかん!」「新しい機能を使いたい!」などの理由からようやっと重い腰を上げバージョンアップに取り掛かることになったのです。
やりかた
手順としては下記のようにしました。
- Laravel Shift(*1)で対象バージョンのパッケージを購入し更新差分のPull requestを作成してもらう
- Laravelの公式のUpgrade guideを確認しLaravel Shiftで漏れているものがないかを確認する
- Pull requestで走るテスト(PHP Unit)をアップグレードするバージョンのLaravelで実行するステップをCIに追加しこけたテストもしくはコードを修正
- CIでアップグレードしたLaravelのバージョンでPHPStan(*2)を実行しエラーになっている箇所を修正
- 動作確認
- 5%カナリアリリース
- 本リリース
この手順を一つ一つバージョンを上げるごとに行いました。
Laravel ShiftはLaravelバージョンアップ用のマイグレーションソフトで、Githubのリポジトリと連携することで対象のソースコードをアップグレードするためのPull requestを作成してくれます。Upgrade guideを見て一つ一つソースコードを探す手間が省けるのでとても便利なソフトウェアです。今回はアップグレードしたいバージョンの分を都度購入し実行しました。
Laravel Shiftが作成したPull request
ただ、たまにLaravel shiftで抜けてしまう更新もあるので、念のためUpgrade guideを確認し気になる更新分は目で確認を行いました。Laravel shiftで作成されたPull requestは変更内容の確認に利用し、実際にはマージしませんでした。
Laravelで実際のフレームワークと実装が離れてしまっている箇所はLaravel shiftのGithub repositoryに置いてくれている素のLaravelリポジトリを参考にしたり、Laravel本体のリポジトリでバージョン間の差分を確認したりしました(参考)。
また、(*2)であげたPHPStanは最初のバージョンアップの際導入を行い、エラーをなくす作業を行っており、それ以降は新しくエラーが出た箇所の修正のみを行いました。こちらに関しては後ほど書こうと思います。
リリースに関しては、一部のユーザーにのみ先にリリースを行ってエラーが起きないかを確認するカナリアリリースで行いました。
カナリアリリースのリクエスト振り分け
バージョンアップ用のサービスを作成しておき、ALBでリクエストの5%を振り分けることで実現しています。今回の場合はカナリアリリース時にエラーが発覚して切り戻しすることはありませんでしたが、リリースするエンジニアとしては影響範囲が狭い段階でエラーの確認をできるため安心してリリースすることができました。
つらかったこと
バージョンアップ作業で辛かったことは、
です。
Laravelのリポジトリで差分を全部確認すればよかったのですが、動作確認の時点でバージョンアップをするとユーザーのセッションが切れてしまうことがわかりました。旧バージョンからの変更でセッションの暗号化ロジックが変更になっていたのですがアップグレードガイドには入っておらず動作確認まで気付くことができませんでした。Laravelのリポジトリにはアップグレード内容が書かれていたのですがそこまで見ていなかったため、危うくリリース時にユーザーのセッションが切れてしまうところでした。
[参考]
https://github.com/laravel/framework/issues/33940
https://github.com/laravel/framework/pull/33662
PHPStanはLaravelの引数・返り値が変更になった際にバグが出ないよう静的に解析しやすくするため導入を決めました。主にphpdocに引数・返り値、またプロパティの型を記載するとソースコード内での整合性を確認することができます。今まで型の宣言は厳しくしてこなかったため、全ての型の宣言を書くのに時間がかかりました。レビューも慣れるまでは時間がかかり、かなり辛い作業でした。
また、プロジェクトとしては最初は他のプロジェクトとも並行してやっていたのですが、なかなかバージョンアップの作業が進まず一旦作業を中止するということがありました。今回私は初めてプロジェクトの旗振りをやったのですが、タスクをエンジニアにお願いするのにバージョンアップの細かい作業はなかなか振りづらく感じてしまい最終的に一度延期となってしまいました。
プロジェクトの一時停止をうけてチームで話し合いが設けられました。その中で「一人一人が複数プロジェクトを並行して進めるのは無理があるのではないか」「今のチームの人数に見合っていないプロジェクト数なのではないか」と問題点を洗い出すことができました。ここで再度バージョンアップの優先度が見直され、一度他のプロジェクトが落ち着いた段階で再開という流れになりました。
そのためキックオフ〜バージョンアップPJ終了まではかなり長い時間を要しました。プロジェクトが軌道に乗るまでは進め方も手探りで作業も多く不安点が多かったですが、チーム全体でプロジェクト進行を話し合えたのは今後のチームでの作業にとって良かったのではないかと思っています。今までは途中で開発作業の差込があった際にプロジェクトの進行に影響が出てしまうことが多くありました。話し合いを経て、差込があったときは稼働しているプロジェクトとの優先順位を見直すことができるようになり、チームのキャパシティを大きく上回る作業が入ることがなくなりました。そのおかげで一つのプロジェクトに集中して取り組むことができ、実装からリリースまでの速度も早くなったように感じています。
やってよかったこと
PHPStanの導入はかなり辛かったですが、入れておいてよかったと思います。エディタでサジェストが分かりやすくなりますし、レビューの際にも変な引数が渡されていないか確認しやすくなりました。
導入に関しては最初はLevel 0で導入し、出てくるエラーを手分けして潰しながらひとつずつレベルを上げていって最終的にLevel 6にしました。Level 6にしてからはCIにも入れ込みreviewdogでPull Requestにコメントがつくようにしています。
reviewdogがPull requestにPHPStan指摘事項をコメント
また、Laravelのバージョンアップの変更で後方互換性のあるものはどんどん本番に混ぜ込んでいく方法を取りました。そのため、バージョンアップ用のトピックブランチとリリースブランチとの差異を少なくすることができ、コンフリクトの修正や動作確認の時間を節約できました。
Laravelのバージョンアップした内容検証をする環境もほぼ本番に近い形で作成でき、CIでトピックブランチにマージされるごとにデプロイされる状態になっています。そのため、確認作業もしやすく、何度か行ったバージョンアップでも利用できたため最初の作成コストはありましたが作成しておいて良かったです。
まとめ
このような流れでLaravelのバージョンアップをすることができました。今回はかなりバージョンが古くなってしまっていたためバージョンアップをプロジェクトとして注力して行いましたが、今後もバージョンはどんどん新しくなるため継続的に新しいバージョンを保つ仕組みづくりが必要だと感じました。
まだまだモダンな環境とは言い難いかもしれませんが、今後も事故なく開発できる環境を整備して新しい機能だったりコンテンツをお客様にお届けできるようにしていきたいと思っています。
現在クラシコムでは一緒に「北欧、暮らしの道具店」を開発してくれるエンジニアを募集しています。
すぐに転職を考えていらっしゃらなくても興味がありましたら是非お話聞かせていただければと思います。お気軽にお声がけください!