社内Railsシステムをアップグレードしつつ、保守性向上のために周辺環境も改善した話

この記事は、プレセナ・ストラテジック・パートナーズ Advent Calendar 2025 5日目です。

エンジニアチームで開発・運用を担当しているkamijoです。

今年、自分の所属するチームのRuby on Railsシステムのアップグレードを担当しました。社内では他にもRailsを使ったシステムがあり、Precena Tech Bookでも過去に紹介したことがあります。
Railsを6.1系から7.0系へアップグレードした時に調査したこと - Precena Tech Book

今回の担当作業では、RubyやRailsのアップグレードに加え、システム周辺の改善も行いました。また、AIを活用してアップグレードに関する情報収集や、AIエージェントによる使い捨てのE2Eテストも作成しました。

Railsアップグレードだけでも工数がかかりますが、合わせて周辺も改善することで、将来の保守が容易になりました。

そこで、この記事では今回行ったアップグレード作業の各ステップ

  • 事前調査
  • アップグレード方針をドキュメントにまとめ、チームメンバーと相談・合意する
  • アップグレードを行う

を紹介します。

 

 

前提となる環境

今回のアップグレード作業の対象となるシステムを取り巻く環境・体制は以下の通りです。そのため、すべてのケースに当てはまるものではありません。

  • 現状、大規模な機能追加は少ない
  • 数人のチームで開発・運用を担う
    • チームメンバーは他のチームにも所属しており、効率的に開発・運用できるのが望ましい
  • 明確なテストコードに対する方針がある
    • ユニットテストのテストカバレッジは、一般的に現実的と言われるラインの80%を超える
    • コストとのバランスからE2Eテストは用意していない
      • ユニットテストである程度カバーできていること
      • カバーできていないところは手動テストで間に合うこと

 

事前調査

依存gemのアップグレードについて

RubyやRailsのアップグレードでは、RubyやRailsの他、システムが依存しているgemのアップグレードも必要になります。

そこで、システムで利用しているgemについて、以下を調査しました。

  • Gemfile.lock を確認し、システムで利用している現在のバージョンを把握
  • 最新のRubyとRailsに対して、問題なく動作するバージョンを把握
    • 問題があるのであれば、回避策なども調査

以前に比べると、生成AIを使うことでこれらの調査は楽になりました。一方で、誤情報の場合には大きな問題になることから、出典を確認したり、各gemのリポジトリにあるissueを確認したりするなど、裏取りも行いました。

 

アップグレード目標とするRubyやRailsのバージョンについて

アップグレード目標は、依存gemがサポートしているRubyやRailsのバージョンです。

その際、RubyやRailsの各EOL、またはRubyとRailsのバージョンの組み合わせに問題がないことを確認しました。

 

社内外の事例を調査

社内には他にもRailsを利用しているシステムがあることから、それらのアップグレードに関する資料やプルリクエストを確認しました。もし、それらを読んだ後により詳しく知りたいと感じたことがあれば、アップグレード担当者に確認しました。

社外事例は、ありがたいことにruby-jpのscrapboxにあるRailsアップグレードガイドにまとまっています。ここに掲載されている記事のうち、今回のバージョンアップに利用できるところに目を通しました。

 

Rails以外の改善できるところがないか調査

今回のRailsアップグレード以外の改善も作業として行える状況だったため、できる限り保守の工数を下げられるような改善ができないか調査しました。

すると、以下のあたりが改善できそうと分かりました。

  • Node.jsをアップグレードすること
  • オーバースペック気味だったJavaScriptライブラリをやめ、ERBとStimulusに置き換えること
  • セキュリティ面やDockerのイメージサイズを考慮した上で、Dockerfileのマルチステージビルドを整理すること
  • GitHub ActionsのRubyのバージョンについて .ruby-version ファイルを参照すること

 

アップグレード方針をドキュメントにまとめ、チームメンバーと相談・合意する

チームメンバーは他のチームと兼務していることもあり、同期的に作業するのはなかなか難しいこともありました。そこで、方針や判断を明文化し、後から振り返ったときに参照できるよう、以下の内容をドキュメントしてまとめました。

  • 調査結果
  • アップグレード作業のフェーズ分けと、各フェーズで何を行うか
  • 相談した内容と、その結果として合意したこと

特に、アップグレード作業は長時間に及ぶことから、作業のフェーズ分離と各フェーズで何をするかについて、チームで合意するのが大事と考えています。

相談の過程や意思決定についてもドキュメントに残すことで、Architecture Decision Record的なものとしても扱えるようにしたり、AIにコンテキストとして渡せるようになっています。

 

アップグレードを行いつつ周辺環境も改善する

続いて、アップグレード方針に従ってアップグレード作業を行いました。

RubyやRailsのアップグレード作業については、Railsガイドやruby-jaのscrapboxを参考にして進めましたが、大きな問題は発生しませんでした。

ここでは、それ以外の内容についてふれておきます。

 

使い捨てのE2EテストをAIとともに作る

今回、アップグレードの影響でシステムが壊れたことを素早く検知できるよう、E2Eテストを作りました。

前提に書いた通り、このシステムにはE2Eテストがなくても通常の開発では問題ありません。一方、一連のアップグレード作業では、Ruby・Rails・gemの各アップグレードをする際にシステムが壊れる可能性があります。

昔だとE2Eテストを作成するコストは高かったのですが、現在では生成AIへ適切に指示すればある程度のものが出てきます。そのため、生成AIとともに使い捨てのE2Eテストを作りました。

ただし、いくら使い捨てであっても、E2E化は正常系のみにとどめました。使い捨てのE2Eテストということもあり、何かあったときのメンテナンスを最小限にしたいためです。

 
E2EテストフレームワークとしてはPlaywrightを使いました。

後で捨てやすいように、ルートディレクトリの下に playwrightというディレクトリを作成し、その中にPlaywright用の設定を入れています。これにより、システムのpackage.jsonへの混入を防いでいます。

E2Eテストを各ステップで実行することで、

  • 予期せず機能が壊れることを検知できた
  • バージョンアップ作業の途中で一部の機能が失われる想定であり、実際、その期間だけE2Eテストが失敗した

ということが分かり、とても役立ちました。

 

テスト仕様書を作る

E2Eテストを作成するのに加え、以下の目的でテスト仕様書を作りました。

  • 確認項目を標準化すること
    • 標準化しておけば、将来のアップグレード作業の参考資料にできるため
    • Playwrightのソースコードに比べると、確認項目の一覧性に優れるため
  • チームメンバーが重要と思ったり、気になっている仕様を明示化すること
    • 各チームメンバーがすべての機能を細かく把握しているわけではないため
  • E2Eテストではカバーしきれない部分を明示化すること

また、テスト仕様書はチームメンバーにレビューしてもらうことで、チームメンバーがシステム全体の機能を把握したり動作確認をしたりするときの参考資料になりました。

 

Dockerfileのマルチステージビルドを整理する

担当システムではAWS ECSを使っていることもあり、デプロイ用のDockerfileが用意されていました。

Dockerfileはマルチステージビルド構成になっていました。しかし、RubyイメージにOSのパッケージ管理ツールでNode.jsをインストールしており、明示的なバージョン指定によるNode.jsのインストールがしづらい状況でした。

今までのマルチステージビルド構成のまま、Node.jsのバージョン管理ツールを追加でインストールすることも検討しました。しかし、バージョン管理ツールの保守やイメージサイズの増大を考慮すると、このタイミングで整理したほうが良さそうでした。

 
そこで、今回は3ステージに分け、各ステージごとに役割を持たせるよう変更しました。

  • ステージ1
    • Node.jsイメージをベースに、JavaScriptまわりをビルド
  • ステージ2
    • Rubyイメージをベースに、gemのビルドステージ
  • ステージ3
    • ここまでのビルドステージの成果物をコピーし、最終イメージをビルド

 
ステージ3で成果物をコピーして利用する都合上、ステージ1とステージ2で使うベースOSは同じものを利用します。

ベースOSが同じであるかどうかは、GitHubのdocker-library/official-imagesにある noderuby を確認します。確認後、同じベースOSとなるタグをDockerfileに指定します。

もし、Node.jsやRubyが古いバージョンの場合は、masterブランチではなく、過去のコミットに遡って確認します。

これにより、Node.jsとRubyのバージョンを適切に扱いつつ、Dockerイメージの軽量化もできました。

 

GitHub ActionsのRubyのバージョンについて .ruby-version ファイルを参照する

GitHub Action ruby/setup-ruby では、開発環境と同じRubyのバージョンでCI/CDを回せるよう、 .ruby-version などのファイルを参照できる設定があります

今回のシステムでは .ruby-versionファイルを使っているため、ruby/setup-rubyのREADMEに従って設定を変更します。

これにより、設定ファイルにRubyのバージョンを定義しなくて済み、Rubyのバージョンアップ時の修正漏れによるCIの失敗が発生しなくなりました。

 

おわりに

チームメンバーと認識を合わせながら丁寧に進めたことで、アップグレード作業が問題なく進められました。また、周辺の改善により、将来のアップグレードがより楽になったと感じています。

先日もRuby on Rails 8.1 リリースノート - Railsガイドが公開されるなど、システムを使う以上、ライブラリのアップグレードはつきものです。

今後も、今回のような形でアップグレード作業を進めることで、最新のRailsに追随し、システムの利用者に安心して使っていただけるようにしていきます。

本記事がRailsアップグレードの参考になれば幸いです。