Upgrade to Pro — share decks privately, control downloads, hide ads and more …

実録レガシー Rails アプリ改善ジャーニー / Legacy Rails app improvement journey

実録レガシー Rails アプリ改善ジャーニー / Legacy Rails app improvement journey

第90回 Ruby関西 勉強会で発表したスライド資料です!
https://rubykansai.doorkeeper.jp/events/150547

shutooike

March 25, 2023
Tweet

Other Decks in Programming

Transcript

  1. 実録レガシー Rails アプリ改善ジャーニー
    2023.3.25 第90回 Ruby関西 勉強会 / 舘林 秀和 @shutooike

    View Slide

  2. 自己紹介
    舘林秀和 / Shuto Tatebayashi
    所属
    株式会社インゲージ
    各種アカウント
    @shutooike
    途中で名字が変わってややこしい
    趣味
    ひとこと
    コミュニティで発表するのが初めてなの
    で緊張してます

    View Slide

  3. 目次
    ジョイン当時の状況
    課題
    改善ジャーニー
    自動テスト編
    バージョンアップ編
    リファクタリング編
    得られたもの
    これから
    まとめ

    View Slide

  4. ジョイン当時の状況
    2014年から開発している Rails アプリ
    フロントエンドはSPA
    バックエンドはAPI+Job
    サービスはたくさんのユーザーに使われている
    Ruby のバージョン2.3
    RSpec が導入されているが、カバレッジは20%以下
    テストがないことがほとんど=レガシーコード
    QAはステージング環境でのドックフーディングと手動テスト頼み
    CIはあった(werkerを使用)
    週に1回本番リリース

    View Slide

  5. 課題
    テストが継続的に書かれてない
    簡単な不具合修正、機能改善、負債返却で他が壊れることがある
    以前と変わることは手動テストで担保するが
    以前と変わっていないことを担保できていない
    ステージング環境によるドッグフーディングには限界が
    変更に対する心理的安全性が低い
    言語・ライブラリのバージョンアップが継続的に行われていない
    EOL切れ
    バージョンアップ時の防衛線がステージング環境にしかいない
    同じようなロジックがあちこちに点在

    View Slide

  6. 改善ジャーニー
    次のスライドから取り組んできたことおおよそ時系列で紹介していく
    この発表で説明しきれない詳細は今後ブログに書いていくつもり
    Twitter で @shutooike をフォローして、読んで、拡散して、感想ください(欲張
    り)

    View Slide

  7. 自動テスト編

    View Slide

  8. 実行順序のランダム化
    ファイル名順での実行になっており順番に依存したテストがちらほら
    spec ファイルを追加すると突然落ちるようになったり
    config.order = :random
    テスト数がそこまで多くなかったのでエイヤで :random
    にして、ローカルやCIで落ち
    たら都度修正
    Kernel.srand config.seed
    とすると Array#sample も seed で再現するようになる

    View Slide

  9. テストデータが残らないように
    CIで特定のテストが失敗することが稀によくある
    他のテストで作ったデータが原因
    example ごとにデータを消す
    config.use_transactional_fixtures = true
    before(:all)
    で作りっぱなしのデータ
    before(:each)
    に変更
    after(:all)
    で削除

    View Slide

  10. 自動テストを書いていく(1)
    日々の開発で自分が関わったコードからテストを書いていく
    なぜか書きにくいという実感が...
    原因突き詰めたところ
    →「そうか!土台が出来てないんだ!」
    プロジェクト発足

    View Slide

  11. 実際の最初の issue

    View Slide

  12. スキップしていたモデルのコールバックを解放
    User.skip_callback(:create, :after, :foo)
    思考停止でのほぼ全てのコールバックがスキップされていた
    プロダクトコードではデータ作成時にコールバックで行っている処理を、テストデ
    ータ作成時は自分で明示的に処理しないといけない状況
    テスト環境の挙動は出来る限り本番環境に近づけるべき
    一部残したコールバックもある
    時間がかかりすぎるもの
    外部サービスを呼び出すもの
    モック化するという手もある

    View Slide

  13. FactoryBot のフル活用
    お馴染みのテストデータ生成ライブラリ
    シナリオに沿ったテストデータをサクッと作れないとテストを書くハードルが一気に上
    がる
    導入はされているが、上手く運用はされていない状況
    FactoryBot の定義方針を検討・決定
    詳しくはブログに書きます
    方針に沿って FactoryBot を再定義・既存のテストを修正
    最初は頑張って全モデルでやろうとしたがその必要はなかった
    よく出てくるスタメンモデルだけでOK

    View Slide

  14. 自動テスト戦略を立てる
    ここまでテストを書いた感覚でテスティングピラミッドを目指すのは厳しいと判断
    単体テストを継続的に書くのは開発者の力量に依存するところが大きい
    良くない単体テストはむしろ開発生産性を下げる
    プライベートメソッドのテストを書く
    期待値生成にコードと同じロジックを用いる... など
    まずは統合テストたくさん書いていくことにする
    目指すはテスティングトロフィー
    主な Rails の責務である API の request spec を書いていく
    とりあえずある程度までカバレッジを上げたい

    View Slide

  15. 誰でも書ける request spec helper を用意
    誰でも書けるように薄さにこだわる
    正常系のみ
    最低限の条件
    ログインユーザー
    パラメーター
    最低限の検証
    APIの権限チェック
    HTTPステータスコードチェック
    APIを作ったら絶対書こうねという意識付け
    →「お約束」というネーミング

    View Slide

  16. 利用側のイメージ
    describe 'POST /api/messages' do
    context '
    お約束' do
    let(:login_user) { create(:normal_user) }
    let(:minimum_required_params) { { text: 'Hi' } }
    it_behaves_like(:can_be_accessed_with, 'normal')
    it_behaves_like(:return_http_status_as, :created)
    end
    end
    どのように実装したかはブログに書きます

    View Slide

  17. 全APIに正常系テスト(お約束)を書いていく
    全体カバレッジが60%ほどに
    APIのカバレッジは90%近くに
    少なくとも最低限の正常系の動作が変わっていないことが担保できるようになったので
    安心感が全然違う
    コスパ最強

    View Slide

  18. request spec でAPIの振る舞いを検証する
    クリティカルユーザージャーニー(CUJ)を定義
    ユーザーが 1 つの目的を達成するために行うサービスとの一連のインタラクション
    CUJをもとにサービスの主要な操作に関するAPIに対して正常系・異常系の振る舞いを検

    振る舞いとは?
    データのCRUD
    ジョブのキック
    外部サービスの呼び出し... など
    主要なAPIの動作の担保され安心感がさらに上がった

    View Slide

  19. 最低限のE2Eテストを書く
    SPAといいつつ複数画面がある
    メイン画面
    編集画面
    外部連携用画面... など
    最低限の検証
    画面が正常に開けることのみ確認
    ステージング環境でのドックフーディングでは普段開かない画面がひっそり死んでるこ
    とに気づけるように

    View Slide

  20. バージョンアップ編

    View Slide

  21. Ruby 2.3 → 2.7.6
    警告の対応
    依存 gem のバージョンアップ
    開発環境でバージョンアップ
    自動テスト・手動テスト
    他の開発者の開発環境に展開
    ステージング環境バージョンアップ
    手動テスト
    本番環境バージョンアップ

    View Slide

  22. Rails 5 → 6
    警告の対応
    依存 gem のバージョンアップ
    開発環境でバージョンアップ
    rails app:update
    new_framework_defaults
    自動テスト・手動テスト
    ステージング環境バージョンアップ
    手動テスト
    本番環境バージョンアップ

    View Slide

  23. リファクタリング編

    View Slide

  24. 複数モデルで使える共通の処理を concern に
    Rails がデフォルトで用意している仕組み
    上手く使えたら綺麗に抽象化できる
    上手く使えたら...

    created_by, updated_by を入れる処理

    View Slide

  25. 単体テストが書きやすくなるような実装に
    自動テスト戦略で統合テストに注力したもう1つの理由として単体テストが書きにくい実
    装になっていた
    ドメインロジックがコントローラー層に
    Fat Controller
    より単体テストが書きやすいようにドメインロジックをモデルに寄せていく必要がある
    Fat Model
    自動テストがある程度書けたので振る舞いが変わらないことを担保しながらリファクタ
    リングができる

    View Slide

  26. [再掲] 課題
    テストが継続的に書かれてない
    簡単な不具合修正、機能改善、負債返却で他が壊れることがある
    言語・ライブラリのバージョンアップが継続的に行われていない
    EOL切れ
    バージョンアップ時の防衛線がステージング環境にしかいない
    同じようなロジックがあちこちに点在

    View Slide

  27. 得られたもの
    テストが書きやすい環境
    APIを作成したらお約束テストを追加する文化
    ある程度のカバレッジ
    コードの変更やライブラリのバージョンアップなどに対する心理的安全性
    他に影響があるコード変更がCIで落ちる
    ライブラリのバージョンアップの非互換に気付けたことも

    View Slide

  28. [再掲] 課題
    テストが継続的に書かれてない
    簡単な不具合修正、機能改善、負債返却で他が壊れることがある
    言語・ライブラリのバージョンアップが継続的に行われていない
    EOL切れ
    バージョンアップ時の防衛線がステージング環境にしかいない
    → 継続してやっていく
    同じようなロジックがあちこちに点在
    → 継続してやっていく

    View Slide

  29. これから
    とりあえず「EOLを切らない」を目標に Ruby, Rails を上げていく
    dependabot をフル活用してライブラリアップデートを習慣化
    テスティングピラミッドを目指す
    単体テストが書きやすくなるよう引き続きリファクタリング
    ステージング環境で行っている手動テストの負荷低減
    現在の最低限のE2Eから少し検証範囲を広げる

    View Slide

  30. まとめ
    自動テストは書かないと上手くならない
    勘所を掴むまでクソテストコードを書いて書いて書きまくれ
    幸いテストコードは書き直しやすい
    レガシーコード改善に銀の弾丸はない
    カバレッジも可読性、メンテナビリティも突然上がることはない
    理想に向けてロードマップを引き地道にやっていくしかない
    レガシーコードを愛する
    ユーザーの課題を解決し、売上を立てるレガシーコードは偉大
    なりたくてレガシーコードになった訳じゃない
    その時々の最適解だったはず
    恨みではなくリスペクトを持って接する

    View Slide

  31. おまけ1
    カバレッジは正しく測ろう
    以下の2つを行ったらカバレッジがグッと上がった
    # config/envioroments/test.rb
    config.eager_load = true
    spring を使わずに RSpec を実行
    原因ははっきりわかっていないがおそらくコードの分母が正しく測れるようになったか

    知ってる人いたら教えてください

    View Slide

  32. おまけ2
    カバレッジを追い求めすぎない
    カバレッジが高いからバグが出ない訳じゃない
    効果的なテストをすることが一番重要
    カバレッジは目標にせず、指標にするべき

    View Slide

  33. View Slide

  34. View Slide

  35. View Slide