はじめまして。2023年4月ごろよりスタメン・TUNAGプロダクト開発にジョインしました、@trowems23です。
つい先日Ruby 3.0がリリースされました。ジョイン初日から数えて829日前のことでした。
Rubyは開発生産性が高いのですが、ブレイキングチェンジの多さに対してライフサイクルがとても短く、積極的なアップデートが求められます。
一方で、スタメン主要プロダクトのTUNAGのコアアプリでのRubyバージョンは2.2から始まり、2.3、2.5と推移して、最後のマイナーアップデートは2021年3月の2.7.2でした(その日時点でのRuby最新バージョンは3.0.0, 2.7.2)。
Ruby 2.7はサポートが2023年3月31日に終了したということもあり、Ruby 3.0以上へのアップデートが早急に求められる状況でした。
原因の分析
既に多種多様なRuby on Railsアプリケーションにおいて、筆者個人は複数プロダクトにおいて何度もRuby 2.x→3.0を実施した経験があったので、迅速に作業だけすることもできましたが、少し背景についても考えたいと思います。
当該のRuby on Railsアプリでは多くのgemを使っていることもあり、2021年時点では依存しているgemのアップデートも難しいため、同年(2021年)中のRuby 3.0へのアップデートは難しかったのではないか、と推測されます。
一方で、スタメンは2022年初よりスクラムチームへの移行を組織レベルで実施したため、プラットフォームである足回りの整備をするリソースは限定的になっていました。
当時の組織開発については、2022年2月にCTOの松谷が書いた、以下の記事をご覧ください。
ゴールの設定
Rubyの最新バージョンは当時3.2.1でした。一気にそこを目指す戦術を取ることもできますが、ゴールは「1秒でも早いタイミングからRuby 3.0.xで動いていること」になります。「1秒でも早い」というのを焦りに変えてもしょうがないので、「次期セキュリティ脆弱性対応パッチがリリースされる『Ruby 3.0.7』のリリースがされるまでの間」と置き換えることにしました。 筆者はRubyにあまり興味がないため、Ruby 3.0.7がいつ出るかの感覚は肌感覚でしか持っていなかったので、このプロジェクトが完了するまでの間(実際には13営業日でした)、プロダクト開発部内でのコンセンサスが形成されることはありませんでした。
手順
- RubyのバージョンをRuby 2.7系の最新版 (2.7.8) にする
- Rubyのバージョンを3.0.6に設定し、CIを流してみる
- 必要なgemのみアップデートする
- 自前で書いたコードの修正を行う
- ワーニングを確認する
- Rubyのバージョンを3.0.6にする
RubyのバージョンをRuby 2.7系の最新版 (2.7.8) にする
これは簡単ですね。2.7.4を2.7.8にしただけです。その差分のチェンジログは大体覚えています。
Rubyのバージョンを3.0.6に設定し、CIを流してみる
Ruby 3.0.6にした状態でCIを流してみたところ、想定通り全く動きませんでした。依存するgemのアップデートを継続的に実施してこなかったため、いくつかのgemをアップデートしないとRSpecなどのテストが開始されない状況でした。
必要なgemのみアップデートする
巷に全てのgemをアップデートするとよいと訴えるRubyプロフェッショナルの方もいらっしゃるようですが、2-3年のブランクを埋めながら、当初のミッションを実現するのは適切ではありません。 手間はかかるのですが、10-15個くらいに留めることで想定外の変更を最小限に抑えることができる、必要なgemのみアップデートを逐一行うことにしました。
自前で書いたコードの修正
1件を除き、全ての修正はkeyword argumentsの修正となりました。当社TUNAGで実装した6コンポーネントにわたり存在していたので、6コンポーネントに対し、1コンポーネント1PRという形で6個のPRにまとめられました。
例外だった1件も URI.escape
(encode
) の修正でしたので、システマチックに実施することができました。が、引数の形式により移行先のメソッドの切り分けをしなくてはならず、オートメーションはできませんでした。ここで、当社業務改善推進プロジェクト「スタラボ」の一環で、GPT-4やGitHub Copilot for Businessの力を援用しようと思ったのですが、該当コードは5箇所しかなく、力づくで直してしまいました。
こういう姿勢はよくないと感じているので、ちゃんとGitHub Copilot for Businessに指示ができるようになりたいなと思ったので、今年の七夕の短冊🎋にはそのように書いておきます。
ワーニングを確認する
gemがアップデートされた状態でstagingでの人間による検証を終えてリリースできるかとおもったのですが、その認識は甘かったようです。
約7年開発されているRails製プロダクトのワーニングを把握するために、マネーフォワードクラウド開発者の方が書かれた「約8年開発されている Rails 製プロダクトを Ruby 3 にバージョンアップするために keyword parameters is deprecated を「網羅的に」検知する方法」を参考にしてワーニングを検知するようにしました。
手順としては config/boot.rb
に以下を記載することで対処しました。
Warning[:deprecated] = true
既に多くのgemがアップデートされた状態であったため、環境による出し分けをすることはなく、開発環境を含め全環境でワーニング出力するようにしました。
リリース
2021年のRuby on Rails 6.1へのアップデート時に利用したcanary環境は手軽に利用できる状態になっていませんでした。手間をかけてしまうとプロジェクト完了までの時間が長くなってしまう(=GW9連休を跨いでしまう)ことが予想されたため、リスクを評価し、通常リリースのワークフローを活用することにしました。ユーザー影響を最小化しつつ、通常営業時間帯を選び、実施することにしました。そのため、今回のリリースにおいて、深夜・早朝や土日休日の作業とはなりませんでした。
Ruby 3.0ベースのイメージのリリースは東京支社内でリモートで行いました。Slack Huddleを活用して、想定外の障害が発生した場合に備えて切り戻しに備えていましたが、特に大きな問題は起きず数時間が経過したため、祝杯を挙げたのち、オフィスそばの辛い麻婆豆腐をおいしく食すことができました。
浮き彫りになった問題
このアップデート作業の中で、いくつかの開発ワークフローに関して問題が浮き彫りとなりました。
これまでのCIのパイプラインでは効率が悪く、開発者体験として悪いことが分かりました。
TUNAGではCI基盤にCircleCIを利用しており、変更内容のみを適切にテストできるようにパイプライン・ジョブを最適化したいと思っています。
また、CDにおいては、CDの途中経過が見えづらくどこで詰まっているのか分かりにくい状況がありました。
TUNAGではAWS Systems Managerを起点にCodeBuild、CodeDeployを利用して、CDパイプラインを構築していました。CircleCIからCDのトリガーしているため、AWS側のCodeBuild、CodeDeployの状況の見通しが悪くなっています。
加えて、図には書かれていませんが、エンジニアの承認のワークフローがあるため、承認が遅れてしまうと、リリースが滞ってしまうことがありました。
これらは今後のCD領域における課題です。
Day 2オペレーション
リリース(CD)したあとの、運用についても改善すべき課題があります。
2016年にプロダクトをリリースしたのち、複数のモニタリングツールを導入してきた経緯もあり、モニタリング・オブザーバビリティにおいて重複・抜け漏れが生まれてしまっていました。
まずはこの状況の整理を行い、TUNAG顧客に適切に価値を提供できる状態を維持しつつ、新機能リリースのスピードアップ・システム運用業務最適化・コスト最適化に繋げていければと思います。
TUNAG開発組織の今後
上に挙げた課題群やこの記事で触れなかった多くの課題を解決するため、スタメンでは開発組織の強化を行なっています。
技術コミュニティ支援の一環として、スタメンは5月に開催されるRubyKaigi 2023にGold Sponsorとして参加します。
また、スタメンnoteでGW明けからリレー形式で毎日記事を公開していく予定ですので、そちらもご期待ください。
参考
- 約8年開発されている Rails 製プロダクトを Ruby 3 にバージョンアップするために keyword parameters is deprecated を「網羅的に」検知する方法(Money Forward Developers Blog)