Timee Product Team Blog

タイミー開発者ブログ

社内版 Rails アップグレードガイドを公開します

こちらはTimee Advent Calendar 2023 シリーズ1の25日目の記事になります。
昨日は @tomoyuki_HAYAKAWA による Swift Concurrency AsyncStreamを使ってみる #Swift - Qiita でした。

タイミーでバックエンドエンジニアをしている id:euglena1215 です。
メリークリスマス🎄 みなさんの手元にはプレゼントは届いているでしょうか。

Ruby の世界では Ruby コミッターサンタさんがクリスマスプレゼントとして新しい Ruby バージョンをリリースしてくれます。 今年は Ruby 3.3 ですね。個人的には 3.3 の YJIT がどれだけ速くなるのか楽しみです。

また、新しいバージョンのリリースにはアップグレードがつきものです。アップグレードせずには新しいバージョンの恩恵を受けることはできません。 ということで、今回はタイミーが社内で運用している社内版 Ruby Rails アップグレードガイドを社外に公開します。

社内版の情報を黒塗りしては社内版を公開する意味がありません。そのため、最大限原文ママでお送りします。前提として足りない部分は「記事用補足」と追記しています。


社内版 Rails アップグレードガイド

このドキュメントは Rails アップグレードのために普段からやること、実際のアップグレードの際にやることをまとめたものです。基本的には一般的な Rails アップグレードガイドと同じですが、QAチェックの必要性など社内特有の事情も考慮されています。

下記のドキュメントは Rails のメジャーバージョン・マイナーバージョンを上げる際に意識すべきことです。パッチバージョンは普段のライブラリアップデート同様に上げてもらって構いません。

🟥 MUST: 安全なアップグレードのために必ず実施しなければいけません。
🟨 SHOULD: 必ず実施しなくてもいいですが、やっておくことを推奨します。
🟩 MAY: 行わなくてもアップグレードは実施できます。関心がある方だけで構いません。

以下の作業は基本的に並列かつ複数人で実施可能です。

普段やること

Rails Edge のキャッチアップ(🟩 MAY)

#guild_rails_edge には毎日 rails/rails の main branch にどんな変更がマージされたのかが流れてきます。ここで次のアップデートでどんな変更が入るのかをチェックしましょう。日頃からチェックしておくとアップデート時に情報の波に飲まれにくくなります。

(記事用補足:ここで流しているのは y-yagi さんのブログです)

y-yagi.hatenablog.com

Rails Edge 起因で失敗しているテストの修正(🟨 SHOULD)

通常の Rails バージョンでは動作するが Rails Edge で動かないテストは pending: pending_if_rails_edge をつけて pending してあります。grep して pending しているテストを見つけて問題を特定し修正しましょう。

(記事用補足:タイミーでは CI で Rails Edge を使ったテストを実行しています。詳しくは以下)

tech.timee.co.jp

Rails へのコントリビュート(🟩 MAY)

Rails Edge 起因で失敗しているテストの修正」の大半はアプリケーションコードに起因する問題であることが多いですが、たまに Rails 本体のバグに遭遇することもあります。その際は Rails にバグレポートや修正 PR を送ることを推奨しています。

リリース済みのバージョンの仕様を変えるのは難しいですが、betaやrc版ではissueやPRを送ることで仕様を変えられる可能性があります。意図しない破壊的変更を見かけた場合は積極的にやりとりしましょう。

参考: Get started with OSS contributions - Speaker Deck

アップグレード前にやること

アップグレード先のCIを用意する(🟨 SHOULD)

アップグレードを検討している頃には既にアップグレード先のバージョンがリリースされているため、Rails Edge はその次のバージョンになっていることが多いです。そのため、Rails Edge CI ではアップグレード先のバージョンでのテストは実行できません。

アップグレード先の Rails バージョンでの CI を用意しましょう。そうすることで、アップグレード先バージョンでのテストが普段から実行できるようになるので問題を早期発見できます。

参考
(ここに Rails 7.1 の CI を用意した Pull Request URL が貼ってありました)

Railsバージョン起因でスキップしているテストをなくす(🟥 MUST)

Rails バージョン起因でスキップしているテストはアップグレード後に動かなくなるテストです。このテストを放置したままアップグレードを実施すると該当機能が動かなくなるので必ずスキップしているテストがないことを確認しましょう。

Rails Edge 起因で失敗しているテストの修正」を実施していればスキップしているテストはほとんどなくなっているはずなので、このステップの対応が楽になります。

参考
(ここにRailsバージョン起因でスキップしているテストを修正している Pull Request URL が貼ってありました)

deprecation warning をなくす(🟨 SHOULD)

ほとんどの問題は「Railsバージョン起因でスキップしているテストをなくす」によって解消されますが、テスト環境では捉えきれない問題も存在します。それらの問題を洗い出すために、タイミーでは本番環境で発生した deprecation warning をログとして出力するようにしています。出力されたログは Datadog Logs で確認できます。

また、ログが出力されすぎて Datadog のコストを圧迫することを防ぐためにログ出力は確率的としています。deprecation warning が減ってきたら合わせて確率を100%に近づけ漏れなくキャッチできるようにすることを推奨します。

rails app:update の実行(🟥 MUST)

Rails ではapp:updateというコマンドが提供されています。Gemfileに記載されているRailsのバージョンを更新後、このコマンドを実行することで、新しいバージョンでのファイル作成や既存ファイルの変更を対話形式で行うことができます。

$ bin/rails app:update
       exist  config
    conflict  config/application.rb
Overwrite /myapp/config/application.rb? (enter "h" for help) [Ynaqdh]
       force  config/application.rb
      create  config/initializers/new_framework_defaults_7_0.rb
...

予期しなかった変更が発生した場合は、必ず差分を十分チェックしてください。

https://railsguides.jp/upgrading_ruby_on_rails.html#アップデートタスク

デフォルトに合わせていた方が今後のアップグレードが楽になるため、既存の挙動をなるべく変化させないように更新分を取り込みましょう。

変更差分を知りたい場合は https://railsdiff.org/ から確認できます。

Rails アップグレードガイドのバージョンに対応した変更を読んで問題ないことを確認(🟥 MUST)

公式の Rails アップグレードガイドにはメジャーバージョン・マイナーバージョンのアップグレードに対して破壊的変更など何らかの対応が必要な変更がまとまっています。これらを確認して問題がないことを確認してください。懸念がある場合は #prd_ch_ruby_on_rails で相談するなど適切な対応を行なってください。

例:7.0→7.1 Rails アップグレードガイド - Railsガイド

参考
(ここに確認したログをまとめた Notion リンクがありました)

リリース前の手動での動作確認(🟥 MUST)

リリース前の手動での動作確認は必須ですが、社内向け管理画面の最低限の疎通確認のみで問題ありません。
背景: (ここに意思決定の証跡として MTG 議事録リンクが貼ってありました)

また、社内向け管理画面においても十分なテストカバレッジがあれば動作確認を省略することができます。社内向け管理画面のテストカバレッジを上げるプロジェクトについては(Notion URL が貼ってありました)を確認してください。

(記事用補足:プロジェクトの Notion URL よりもTimee Advent Calendar 2023シリーズ1の5日目記事の方がより詳細にプロジェクトの説明をしています。興味があればご覧ください。)

tech.timee.co.jp

アップグレード作業中に行うこと

アップグレードの事前周知(🟥 MUST)

Rails アップグレードのリリース直後に rollback が困難な変更が行われると Rails アップグレード起因で問題が発生した際に rollback が困難になり、問題の復旧が遅れます。そのため、アップグレードを事前に周知しアップグレード後30分間はデプロイ(=マージ)を行なわないよう呼びかけてください。

rollback 用のコミットハッシュを取得する(🟥 MUST)

問題があった際の復旧を早めるために rollback で戻す先のコミットハッシュを用意しておきましょう。https://github.com/x/x/commits/master から確認できます。

Revert PR を作成する(🟩 MAY)

問題があった際の復旧を早めるために merge 後すぐに Revert PR を作成しておくことを推奨します。先に Revert PR を作っておけば feature branch での CI 待ちをショートカットすることができます。

問題が発生せずに無事リリースできた場合はきちんと PR を close しておきましょう。

各種監視を行う(🟥 MUST)

リリース直後は問題を早期発見するために各種の監視を行なってください。15分様子を見て問題が見つからなければ急いでrollbackする必要はありません。

  • Sentry
    • 普段見かけないエラーが発生していないかどうか
  • Datadog
    • ダッシュボード
      • 大きなメトリクスの変化がないかどうか
    • APM
      • レイテンシやエラーレートに変化がないか

(記事用補足:Sentry や Datadog など各種 URL が貼ってありましたがさすがに削除させていただきました)

アップグレード後にやること

new_framework_defaults_x_y.rbの有効化(🟨 SHOULD)

Rails をアップグレードしただけでは多くの新しい機能は有効化されていません。 rails app:update で作成された new_framework_defaults_x_x.rb のコメントアウトを1つずつ確認してデフォルトに合わせられそうなものは有効化しデフォルトに合わせ、意図を持ってデフォルトとは異なる設定を行う場合は application.rb に設定を追記しましょう。

config.load_defaultsの更新(🟨 SHOULD)

app:updateタスクでは、アプリケーションを新しいデフォルト設定に1つずつアップグレードできるように、config/initializers/new_framework_defaults_X.Y.rbファイルが作成されます(ファイル名にはRailsのバージョンが含まれます)。このファイル内のコメントを解除して、新しいデフォルト設定を有効にする必要があります。この作業は、数回のデプロイに分けて段階的に実行できます。アプリケーションを新しいデフォルト設定で動かせる準備が整ったら、このファイルを削除してconfig.load_defaultsの値を反転できます。 https://railsguides.jp/upgrading_ruby_on_rails.html#フレームワークのデフォルトを設定する

参考

railsguides.jp

qiita.com


いかがでしたか?

タイミーは上記の Rails アップグレードガイドを用いて Rails アップグレードを行なっています。とは言ったものの、このアップグレードガイドは Rails 7.1 アップグレードでやったことを体系的にまとめたもので本当に運用され始めるのは Rails 7.2 アップグレードのタイミングになります。
Rails アップグレードは属人的なタスクになりがちです。特定の人物がアップグレードの番人になるのではなく、誰でもアップグレードにチャレンジできるようにしたかったのがアップグレードガイドを作成した背景になります。

ネットには質の高いRailsアップグレードガイドがたくさん存在します。ですが、どのステップをどんな期待値で実施するか・動作確認はどこまでやるかは各社の状況に依ると思います。社内の暗黙値を減らすためにも社内版 Rails アップグレードガイドを作ってみてはいかがでしょうか。