仕事で行うRuby/Railsのgem update手順

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110571/647100af-6758-cb7e-59fe-22ce4cf8e5e8.png)

# はじめに
こちらの記事はRuby Advent Calendar16日目の記事です。
[平成RubyKaigiではしゃぎすぎた](https://twitter.com/terry_i_/status/1205784546957905920)結果、この記事を期限ギリギリに書いていますが、後悔はして~~いる~~いません。

最近、会社のお仕事でgemのアップデートを行うことが多いのですが、ググったところgemのアップデート手順が意外と少なかった（正確に言うと、個人でのアップデートか会社でのアップデートかがわからないものが多かった）ので、自分の行っている作業をメモがてら書いてみることにしました。

「お前、そんなザルなやり方じゃ痛い目見るぞ」というマサカリ等あればヴァッシヴァシと飛ばしていただければと思いますので、Rubyのgemアップデートを初めて仕事で行う方がこの記事を読んで、何か得られるものがあれば幸いです。

> ※注：本記事で記載している手法はあくまで個人的な進め方であり、所属する組織や企業が推奨/強制するものではありません

# 前提
- Railsを用いたWebアプリケーションを開発していること
- プロダクトコードの動作が、テストコードでである程度担保されていること
- [dependabot](https://dependabot.com/)や[circleci-bundle-update](https://github.com/masutaka/circleci-bundle-update-pr)などで、gemのバージョン差分を検知しPRが作られる環境であること
    - PRの自動作成まで進んでいない場合は、ローカルで `bundle update {gem名}` した `Gemfile.lock` をプッシュしPRを作る必要があります

>※参考になる記事

[<img width="626" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110571/6572e514-d74c-6c2c-cb5c-0acd6b30da51.png">](https://dev.classmethod.jp/etc/dependabot-101/)

[<img width="628" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110571/eb4ca00d-051e-ab8f-08ba-5b1dc0061375.png">](https://made.livesense.co.jp/entry/2017/11/07/090000)

# 今回は話さないこと
- gem updateを行うための組織的な課題の解決について
    - 経営陣や事業サイドなど社内の同意をどう取っていくか
    - アップデートを定期的に行っていくためのリソースをどう確保するか
    - メンバーの作業へのモチベーションをどう維持するか
- updateできる環境の構築
    - CI/CDや各種ツールの導入
    - プロダクトコードのテストによる品質の維持

# gem updateに取り組むメリット
メリット云々以前に、ちゃんと実施していかないと長期的にサービスが死ぬのですが、とはいえ何やかんや上げるのをすっぽかすパターンもあると思います。そのため、一個人のエンジニアとしてgemのアップデート作業に取り組むメリットを提示し、テンションを上げておきます。

- gemの知見が溜まる
    - 普段触らない＆調べないgemに触れるいい機会になります
    - バグやイケてない箇所を見つければPRチャンスに繋がるかも
- プロダクトコードへの理解度が高まる
    - gemの影響範囲の調査を通じ、既存コードのキャッチアップが進む
    - プロダクトで活用しているgemのキャッチアップが進む
- 芝が生やせる
    - アップデートに伴うCommitやPRのレビューなどもGitのcontributionになります

# 手順
## 1.対象となるgemとバージョン差分を確認
<img width="663" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110571/384f0a7a-9a60-9c28-80d1-484b750ff1dc.png">
> ※画像はdependabotを用いたPRです。それ以外のツールの場合は、gemごとに個別にアップデートするPRを上げるのが良いです（一斉に上げると影響範囲が置いづらくなるため）

- まずはアップデートの対象となる「gem」と「バージョン差分」を確認しましょう
    - gemを確認することで「サービスのどの部分に影響を与えるものか」を把握し、後の動作確認に活かします
    - 「バージョン差分」で、どれくらい重そうな作業になるかをなんとなく覚悟できます（当然最新バージョンとの差が開けば開くほどつらくなります）

## 2.CIのステータスをチェック
- RspecやrubocopなどのCIの通過状況を確認します
- （プロダクトコードでテストが書かれている場合）CIがパスしていれば「基本的な動作には影響がない」と判断する事ができます
- 仮にCIが落ちている場合は、該当箇所の修正が必要です
    - gemのChangeLogやCommit内容などを確認し、どのように記述方法を変更すべきかを確認しましょう
    - ローカルでCIが通過すれば、通常のPRと同じようにリモートリポジトリに変更差分をPushします
- testやdevelopment環境でのみ動作するgem（例：[factorybot](https://github.com/thoughtbot/factory_bot)など）であれば、CIが通った段階でマージしてしまっても問題ないでしょう

## 3. ChangeLogやCommitsをサッと確認する
<img width="1083" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110571/240e3e79-7496-da2e-3d57-59f76225a11b.png">
> ※addressableのcommit compareviewの例

- OSSであるgemには必ず「ChangeLog」と「Commits」があるので、そちらを上から眺めます
- 基本的には該当するバージョンアップに「**破壊的変更**」がないかを確認する
    - 破壊的変更：既存の動作に大きな影響を与える変更のこと
    - ドキュメントが丁寧なgemであれば、ChangeLogに `Breaking changes` 的な記述がされているので、そのコミット内容を確認し、プロダクトコードに関連する機能が使われていないかを確認する
    - ChangeLog上でそれらしい記述がなければ、Commitsをcompare viewで確認し、削除されたクラス・メソッドを追う（プロダクトコードに使われていそうな機能のコードなら要注意）
- プロダクトにおけるgem updateの目的は保守であり、既存の動作に影響がないようアップデートすることが最優先
- メジャーアップデートを行う際は `gem名 バージョン名` でググってみたり、gemのissueまで確認しておくとよいでしょう（思わぬ落とし穴があるかも知れないため）

## 4. ローカルやstg環境での動作確認を行う
- `git fetch` してリモートPRをローカル環境に取り込み、 2や3で確認した自社プロダクトで該当gemを使用した挙動を確認する
    - 例：[devise](https://github.com/plataformatec/devise)や[sorcery](https://github.com/Sorcery/sorcery)でユーザーのログイン＆ログアウト、ユーザーの新規作成などを行う
- ローカルでの動作に問題がなければ、stg環境に変更内容を反映し上記と同様の検証を行う
    - 対象のgemがパッチアップデートで、CIがパスしておりかつCommitLogなどで非常に軽微な変更であること（例：テスト追記やrubyのバージョン対応追加）が明確である場合は、ローカルもしくはstg環境のどちらかの検証を飛ばしてしまっても良いでしょう
    - 検証を誰が行うかですが、gemのupdateはあくまで開発者の事情であり、かつgemのプロダクトでの動作をある程度把握していることが必要なため、基本的には開発者が行うのが良いと考えます
- 特にメール配信などのバッチプログラムを実行する際は、面倒ですが必ずstg環境で動作を検証しましょう
    - バッチの動作性質からアップデートの失敗に気づくのが遅れたり、原因がgemのアップデートにあることにたどり着くために時間を要する可能性が考えられるため

## 5. 確認事項をPRのコメントに記述し、マージ＆デプロイ
<img width="759" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110571/1ee660c3-4b60-e3c9-66a8-e3a30f954b34.png">
> ※お仕事で[addressable](https://github.com/sporkmonger/addressable)を `2.6.0`から`2.7.0`へ上げたときのPRでのコメント

- 「何をどう確認して」「その結果どう判断したのか」をPRのコメントに残します
    - アップデート失敗時などに影響範囲を調査したり、gemのアップデートを取り消すorプロダクトコード側を変更する判断を下すかの初動対応の材料になるためです
    - 仮に調査が途中で終わったり、アップデートを先延ばす判断をした場合も同様
- アップデートする判断を行った場合は、マージしデプロイしましょう
    - アップデートが原因でバグや不具合が出た場合に、「このgemのアップデートが原因だ」と想起できるように、できればデプロイはApproveをした当日に毎事＆デプロイするのがベストです
    - 想起させるのが大切な理由として「gemは厳密には自社のプロダクトコードではないため、それと比較して把握度や理解度が浅いことが多いから」と考えています
- 以下、PRに残すコメントのテンプレです（参考になれば）

```markdown
# 何を確認したのか（全部チェックされていればベスト）
- [ ] ChangeLog
- [ ] CommitLog
- [ ] CIのパス
- [ ] ローカル環境での動作確認
- [ ] stg環境での動作確認
- [ ] issue
- [ ] Google検索

# 動作確認を行う手順

# 関連URL
- CommitLogのURLやローカル/stgでの実行結果やスクショなど

# どう判断したのか
- [ ] Update
- [ ] Pending
```

# さいごに
以上、私はこんな感じでgemをアップデートしております。

「他にもこんなやり方あるよ」って方いらっしゃれば、ぜひコメントなどで教えて頂けますと幸いです。

gemのアップデートは放置してしまうと、古いバージョンのものからバンバンとメンテナンスが切られてしまい、最終的には一定期間のコードフリーズ（新規開発の停止）やシステム全体をリプレース...ということにもなりかねません。

せっかくRubyやRailsを使って開発をしているのなら、数々のgemやモジュール等の優れたコミュニティを利用しない手はありません。きちんと定期的にアップデートして、快適なRuby生活を送りましょう！