テストカバレッジを30%上げてRails 4.2から6.0へのアップデートを決断をした話

はじめに

こんにちは!マーケティングテクノロジーDivの遠藤です。

今回、私が運用/開発に携わっている社内システムのRuby、Ruby on Rails(以下 Rails)の
バージョンアップを以下の通り実施したのでそれについて話します。

  • Ruby 2.3から2.7へ
  • Rails 4.2から6.0へ

バージョンアップが必要だった背景

利用していたRuby、Railsのバージョンの各リリース日、
サポート・ライフサイクル(以下EOL)は、以下の通りです。

  • Ruby 2.3.0
    • リリース日: 2015年12月
    • EOL: 2019年3月31日
  • Rails 4.2.0
    • リリース: 2014年2月
    • EOL: 2017年4月27日

このように約7〜8年前に出たバージョンを利用し続けている状態であり、
EOLについても数年前に切れていました。
そのため、セキュリティリスクはもちろんのこと、
近年は特に以下のことを問題と感じるようになっていました。

  • 利用したい機能がRails 4.2のバージョンでは提供されておらずバージョンを上げる必要がある。
  • Ruby 2.3がgemのサポート対象外となっているものが増えた。

バージョンアップ実施を決断できた要因

バージョンアップ実施を決断できた要因として以下4つ挙げられると考えています。

1. テストカバレッジが高まったこと

テストカバレッジはCIで値を取得し始めた2020年4月時点で30%程度でした。
これを継続的なモブプロ形式でのテストコードの追加やデッドコード削除を通して
1年半ほどかけて60%程度まで向上させることができました。

2. チーム状況の変化

開発メンバーとして新卒1名を受け入れることができ、
通常の運用、開発案件を任せることができるようなりました。
そのため、チームとして開発リソースの余力が以前よりできたタイミングでした。

3. 組織施策の後押し

組織としてシステムのバージョンアップを促す活動が実施された時期でした。
この活動によりバージョンアップ作業実施を後押ししてもらえたと感じています。
結果としてこの支援のおかげで、心折れずにバージョンアップ作業を継続することができたと思います。

4. システムの習熟度が高まった

私がシステムに携わってから2年半経過しており、仕様面やシステム構成について理解度が十分に高まっていました。
バージョンアップ後に何か問題が生じてもチームとして解決できる自信が身についた時期でした。

対象システムの規模感

システムの規模感としては中規模程度になるかと思います。
参考程度に次の情報をお伝えします。

ファイル数、ルーティング数

  • モデルファイル数: 163 ファイル
  • ルーティング数: 370

テスト

  • カバレッジ: 63%
  • テスト件数: 1,711件

バージョンアップ差分

Ruby 2.3から2.7、Rails 4.2から6.0にアップデートした後の
最終的な差分は以下になりました。

  • 変更ファイル数: 986
  • 追加行数: 3,838
  • 削除行数: 9,867

アップデート作業について

大まかな手順としては下記の通りです。

  1. Rails関連 gem update
  2. ローカル環境下でRails Serverの起動ができるよう修正
  3. rspec の実行と修正

基本的に rspec を実行後に発生したDEPRECATION WARNINGを
ひたすら対応していくということを繰り返した形です。

実施スケジュール

以下のようなスケジュールで3回に分けてリリースしていきました。

全アップデート作業期間として約1年間かかっていますが、
リリース後の様子見期間として1〜2ヶ月置き、その期間に別タスクを実施しています。
そのため、一年間アップデート作業のみ行っていたかというとそうではありません。

Pull Request は以下の単位で出してレビュアーに確認してもらいました。

  • Rails 4.2から4.2系最新
  • Rails 4.2から5.0
  • Ruby 2.3から2.4
  • Ruby 2.4から2.5
  • Ruby 2.5から2.6
  • Rails 5.0から5.1
  • Rails 5.1から5.2
  • Rails 5.2から6.0
  • Ruby 2.6から2.7

実施してみて所感

1. monkey patchしているgemのアップデート作業は辛い

Rails 4.2から5.0へのバージョンアップ作業でエラーが発生しました。
このエラーは、ActiveResourceというgemをmonkey patchしていたことで、
オーバーライドしていたメソッドに変更が入ったため発生していました。
今回、このgemがmonkey patchされていることが把握できていなかったため、
エラーの原因を特定するのに時間を要してしまいました。
また、このオーバーライドしているメソッドの使用箇所が多かったため、
動作検証についても慎重に実施する必要がありました。

2. Ruby は想定よりスムーズにバージョンを上げられた

Rubyについては基本的に各バージョンごとにdeprecatedになっているものを対応していくだけで
問題なくバージョンを上げていくことができました。

3. 2世代先のバージョンアップの作業見積もりが立てにくい

1世代のバージョンアップについては、Railsのgemをアップデートした後に rails srspec を実行して
発生してエラーから実際の影響箇所を把握できます。
しかし、2世代以上先のこととなると、その手順による影響箇所の把握ができないため
Rails アップグレードガイド などの
アップデートに関する情報をもとに影響箇所を調査し、作業ボリュームを想定する形で進めました。

4. バージョンアップ作業は短期決戦!!

バージョンアップ作業についてはやはり少しずつ作業を進める、例えば毎週1日だけ時間を確保して
作業を進める、といった対応がとりわけ取りにくい作業だなと強く感じました。
理由としては、対応している途中で別ブランチで開発が進んでしまい、
そちらに変更、追加があったコードに対しても最終的にバージョンアップ対応が必要になるからです。

また、ある程度まとまった時間を確保してアップデート作業を実施できたとしても、レビューに時間を要してしまうと、
その期間で別ブランチで開発が進んでしまい、そちらの修正が必要になってしまいます。
さらに修正したものに対してもレビューをしてもらって....と悪循環に陥ってしまいます。
このため、Ruby、Railsのバージョンアップ作業についてはチーム内で比較的高い優先度のタスクだという認識を持って臨むことが
非常に大切だなと感じました。

バージョンアップして良かったこと

1. Rubyのバージョンが古くてgemを入れられない問題解消

前述しましたRuby 2.3をサポート外にしているgemが増えてきていたので
この問題が解消されました。

2. 秘匿情報管理が楽にできるようになった

Rails 5.2から追加されたcredentials.yml.encを使うことで
秘匿情報管理を簡単にできるようになりました。

3. システムに対してポジティブになれた!!

個人的にはこれが一番大きいメリットと感じています。
システムがこのようにレガシーなものだと日々、「古い」「利用した機能がこのバージョンでは使えない」
「セキュリティリスクが」「遅い」「FIXされていないバグがある」などなど、ネガティブな思考をしやすい環境下に置かれることとなります。
この日々の思考が精神に与える悪影響としては、決して過小評価してはいけないことだな、と強く感じていました。

今後の展望

1. Zeitwerkモード対応

Rails 6.0から提供されているZeitwerkモードですが、移行の影響箇所が大きいため、
Rails 6.0とのリリースを分けて何か問題が発生した場合に原因の切り分けがしやすいようにするため、作業を見送りました。
Zeitwerkへの移行はRails 7.0のアップデートまでに実施しなければならないので、
それまでに移行を済ませていたいと考えています。

2. Ruby 3.0, Rails 6.1 リリース対応

Ruby 2.7のEOLが2023年3月まで、Rails 6.0のEOLが2023年6月までとなっているので、
それぞれEOLが切れる前にアップデートできるよう2023年の開発スケジュールに組み込みたいと考えています。

3. Ruby / Rails のアップデートは少なくとも年1で実施

Ruby、Railsのリリースサイクルとして現在年1回新しいバージョンが出るのため、
少なくとも年1度アップデート作業を実施できるよう開発チーム一同取り組んでいき、
今回のようにまとめてアップデートするようなことにならないようにしていきたいです!!(涙目)

4. monkey patchしているActiveResource gemの依存排除

今回特にアップデート作業として苦しめられた箇所の一つなので
次のアップデート時までにはこの依存を排除し、快適にアップデートできるようにしたいと考えています。

5. カバレッジのさらなる向上

現状のカバレッジは63%と決して高い数値ではないと考えています。
今回のアップデート作業においても、テストが存在しない箇所の動作確認については手動で実施しました。
今後はこういった手動での動作確認をできるだけ減らせるようテストを追加していきたいと考えています。

おわりに

今回、リリースされてから一度もアップデートされていないシステムのRailsバージョンを
まとめてサポート期間内のバージョンへとアップデートしていきました。
約7〜8年分ものバージョンアップになるので、作業ボリュームとしては非常に大きくなることが想定されました。
そのため、バージョンアップ実施の判断をチームとして決断することは決して簡単ではありませんでした。
その決断をすることができた要因として大きいものは、やはりチームメンバー全員で、日々継続的にテストコードの追加と
デッドコード削除やリファクタリングをしていくことでテストカバレッジを大きく改善できたことだと考えています。
今後もこのテストカバレッジを高めつつ、これを当システムの一つの武器としてバージョンアップ作業に取り組んでいきたいと考えています。