チームでフレームワークのバージョンアップに立ち向かうための考え方

こんにちは、こんちゃん(@konchanSS)です。


Zucks アドネットワークでは広告配信プラットフォームの開発をしています。その中で、マイクロサービス的にいくつものサービスに分割されて運用されています。

cartaholdings.co.jp

Zucksが大切にしているエンジニア文化はこちら

今回は、管理画面サービスで行ったフレームワークのバージョンアップについて書いていこうと思います。特に、進め方、考え方について伝えます。

今回バージョンアップを行ったのは、SymfonyというPHPのフレームワークです。

目次

バージョンアップにおける考え方

バージョンを上げて終わりではない。いかに上げやすく作っておけるかが大事

いきなりですが、バージョンアップはサービスが継続している限りずっと付きまとう問題です。
これをいっては元も子もないかもしれないですが、バージョンを上げること自体は誰でも頑張ればできてしまうことです。

大事なのは、上げやすいチームや仕組み、コードをいかに作っていけるかどうかだと思います。

具体的にどうしていくかという話は以下に書いていきます。

人の手による作業をできるだけ発生させない

バージョンアップで一番地道で大変なのは「新しいバージョンに合わせたコードに書き換えていくこと」だと思います。

ここは温かみのある手作業が必要になることが多いですが、言語やフレームワークによっては自動で対象コードを変換してくれるツールがあります。

そこでツールを全力で頼っていきました。
幸運なことに私たちの管理画面サービスで使っているSymfonyには、"Rector" というツールがあり、それを使うことである程度の変更はツール側で追従することができました。

github.com

使った中で得られた所感を以下にまとめました。

  • 考え方
    • ツールの信用しすぎを避ける。
    • 動作確認、ユニットテスト、E2Eテストでサービスの品質は担保する。
  • 使ってみてどうだったか
    • 200ファイル以上の変更が必要な対応を手作業なしで実現できた。
    • Rectorの変更をそのまま使うと、アプリケーションがうまく動かなくなるケースもあったが、不具合に気づける仕組みがあれば、手作業よりは効率が良い。


ユニットテストとE2Eで動作を担保する

動作確認の話に触れたので、次はユニットテストとE2Eの話をします。

基本的に修正したコードの動作確認はほぼ手で行わず、ユニットテストとE2Eによる確認を主体としていく方針を取りました。

動作確認も実際のアップデートの修正と同じ様にかなり時間を取られてしまい、かつ温かみのある手作業が必要になるところです。

スプレッドシートなどでリストを作って、1つ1つの画面を確認テストしていくのはかなり辛いですよね。

なので 動作確認も自動化できそうなところはしていきました。

自動化においては、以下のことを意識していました。

  • 実際の生リクエストとE2Eのテストデータに乖離がないよう実態に合わせる。
  • どうしても実際にアクセスして確認できるまで怖いというのであれば、実際のリクエストをコピーして新しい環境に投げてみる方法を採用する
    • 具体的にはNginxのミラーモジュールなどを使って実際のリクエストを新しい環境にも流してみる

バージョンアップする際にビッグバンリリースをしない

新しいバージョンに向けた変更をどんどんしていくときに気をつけることがあります。

変更を、新しいバージョン用のブランチを切ってそこに溜めていくと、一気にリリースすることになるので当然手戻りも発生しやすくなるということです。

必ずしも変更をためていくこと自体が悪いわけではありません。
気軽に失敗して戻せる環境があるのであれば、直す→戻す→直すを繰り返して最終的にバージョンを上げるというやり方もあります。

私たちのチームでも過去にそのようなバージョンアップ方法を実施したことがあります。

小さく作ったものを小さくリリースしていく

しかし、小さくすすめることができれば以下のメリットがあります。

  • 小さなPullRequestをマージして小さく進める
  • 心配事や手戻りが少なくなり安心しながら進められる。
  • チームでバージョンアップを行う際に、タスクを小さくまとめることができるのでチームメンバーへの割り振りやレビュー時の負担も軽減できる

具体的に、私たちのチームは以下のケースで小さくPullRequestを作成してバージョンアップ前にどんどんマージしていくことにしました。

  • フレームワークのパッケージ単位で新旧両方で動くコードに書き換える
  • 使っているライブラリも新旧両方で動くバージョンまで上げる


バージョンアッププロジェクトの進め方

考え方については以上で、次は実際のプロジェクトの進め方について書いていきます。

まずは、一次情報に触れる

まず、バージョンアップに取り組む前に、公式ドキュメントなどの一次情報に触れることが大切です。
特にフレームワークによっては、バージョンアップに伴う変更箇所が公式ドキュメントにまとめられている場合もあります。
また、一次情報以外にも他会社での同様の事例も記事になっていることがあるので見ておきましょう。

ロードマップを考える

次にやるのはバージョンアップのロードマップを考えることです。
Zucks アドネットワークの場合、現在バージョンから最新バージョンの間に複数のメジャーバージョンがありました。
なので、最終的なバージョンの目標を設定し、そこに至るまでどのバージョンを挟んでいくのが良いのか検討しました。
バージョンを挟んでいくという判断は、以下のことに気をつけながら考えました。

  • 目標バージョンに辿り着くまでの複数バージョンで同じパッケージの変更がないか?
    • 以下を確認する
      • 特定のバージョンに上げる際に修正した内容を、結局別の上位のバージョンでも修正する必要がある。
      • もしくはその上位バージョンでなくなってしまう
    • もしそうならまとめてやってしまう
  • 破壊的な変更が入るタイミングがないか?
    • もしあるならその一歩手前のバージョンまで一旦上げて、破壊的変更のあるバージョンはそれ単体で上げる
    • あるいは、ある程度影響範囲が限られるならまとめてやってしまうのも一手


スコープを絞る

全てのコードがバージョンアップに必要なわけではないため、バージョンアップ対象のスコープを絞ることで影響範囲を減らすことができます。
具体的には、使用されていないエンドポイントの削除やコード整理を行いました。
今回適用しなかった検討事項としては、アプリケーション自体を分割し、別の基盤に載せてバージョンアップのリリースを複数に分けることもあげられました。


チームのあり方

では、実際にどのように「バージョンアップをチームで取り組むか」について書いていきます。
チームへ文化を浸透させる方法は、以下の記事にまとめています。

techblog.cartaholdings.co.jp

バージョンアップを属人化させない

これまで私たちのチームでは、特定のフレームワークに詳しいメンバーがいて、バージョンアップはその人たちに任せっきりで属人化していました。
その方が部署異動や退職された結果、チームとしてのバージョンアップに対する知見や仕組みが残っておらず、バージョンアップを放置してしまっていた過去があります。

対策としてバージョンアップの知見などが偏らないように バージョンアップ専任というポジションを作らず、チームとして取り組むようにしました。また、チームがバージョンアップを進めた結果、得られた知見について共有する時間を設けていました。

まとめ

今回は私たちのチームにおける、フレームワークのバージョンアップを進める際の考え方について書いていきました。

上記にも書きましたが、バージョンアップの問題はサービスが継続していく限りずっと続く話ですので
どう上げやすくしておけるか、また上げやすさをどう仕組みやチームで担保していくかがとても大事だと思います。

余談

いざとなったら諦めるという選択肢を残しておく

バージョンアップによる恩恵は大小様々あると思います。(脆弱性の面や、フレームワーク機能充実による開発効率の向上面などなど)

もし、バージョンアップによる恩恵よりも上げるつらさが勝る、上げること自体が困難な場合は諦めてしまうのも一手です。(あまりオススメはしませんが)

諦める場合はもちろん、自分達で脆弱性対応のためのパッチなどをあてていく覚悟がそれとは別に必要になってきます。

アプリケーションの特定の処理をアタックサーフェイスが限定的である別基盤に乗せてしまい一旦塩漬けすることもできないかあたりは弊チームとして考えていました。
もちろんこのパッチをあてていくというのはサービスの寿命とセットで考えていかないとどのみち辛くなってくると思います。

以上です。

採用について

Zucksではエンジニアを募集しています。問い合わせはこちら。

hrmos.co

techblog.cartaholdings.co.jp