Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
実録レガシー Rails アプリ改善ジャーニー / Legacy Rails app impr...
Search
shutooike
March 25, 2023
Programming
3
850
実録レガシー Rails アプリ改善ジャーニー / Legacy Rails app improvement journey
第90回 Ruby関西 勉強会で発表したスライド資料です!
https://rubykansai.doorkeeper.jp/events/150547
shutooike
March 25, 2023
Tweet
Share
Other Decks in Programming
See All in Programming
ソフトウェアエンジニアの成長
masuda220
PRO
12
2.1k
Rubyで始める関数型ドメインモデリング
shogo_tksk
0
130
Grafana Loki によるサーバログのコスト削減
mot_techtalk
1
140
Go 1.24でジェネリックになった型エイリアスの紹介
syumai
2
260
AIプログラミング雑キャッチアップ
yuheinakasaka
14
3.6k
GitHub Actions × RAGでコードレビューの検証の結果
sho_000
0
290
Djangoアプリケーション 運用のリアル 〜問題発生から可視化、最適化への道〜 #pyconshizu
kashewnuts
1
260
PHP ステートレス VS ステートフル 状態管理と並行性 / php-stateless-stateful
ytake
0
110
CI改善もDatadogとともに
taumu
0
180
Flutter × Firebase Genkit で加速する生成 AI アプリ開発
coborinai
0
170
クリーンアーキテクチャから見る依存の向きの大切さ
shimabox
4
920
XStateを用いた堅牢なReact Components設計~複雑なClient Stateをシンプルに~ @React Tokyo ミートアップ #2
kfurusho
1
960
Featured
See All Featured
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
4
430
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
44
7k
Why Our Code Smells
bkeepers
PRO
336
57k
Stop Working from a Prison Cell
hatefulcrawdad
267
20k
What's in a price? How to price your products and services
michaelherold
244
12k
How to train your dragon (web standard)
notwaldorf
91
5.9k
Scaling GitHub
holman
459
140k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
29
1k
[RailsConf 2023] Rails as a piece of cake
palkan
53
5.3k
Docker and Python
trallard
44
3.3k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
33
2.8k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
27
1.9k
Transcript
実録レガシー Rails アプリ改善ジャーニー 2023.3.25 第90回 Ruby関西 勉強会 / 舘林 秀和
@shutooike
自己紹介 舘林秀和 / Shuto Tatebayashi 所属 株式会社インゲージ 各種アカウント @shutooike 途中で名字が変わってややこしい
趣味 ひとこと コミュニティで発表するのが初めてなの で緊張してます
目次 ジョイン当時の状況 課題 改善ジャーニー 自動テスト編 バージョンアップ編 リファクタリング編 得られたもの これから まとめ
ジョイン当時の状況 2014年から開発している Rails アプリ フロントエンドはSPA バックエンドはAPI+Job サービスはたくさんのユーザーに使われている Ruby のバージョン2.3 RSpec
が導入されているが、カバレッジは20%以下 テストがないことがほとんど=レガシーコード QAはステージング環境でのドックフーディングと手動テスト頼み CIはあった(werkerを使用) 週に1回本番リリース
課題 テストが継続的に書かれてない 簡単な不具合修正、機能改善、負債返却で他が壊れることがある 以前と変わることは手動テストで担保するが 以前と変わっていないことを担保できていない ステージング環境によるドッグフーディングには限界が 変更に対する心理的安全性が低い 言語・ライブラリのバージョンアップが継続的に行われていない EOL切れ バージョンアップ時の防衛線がステージング環境にしかいない
同じようなロジックがあちこちに点在
改善ジャーニー 次のスライドから取り組んできたことおおよそ時系列で紹介していく この発表で説明しきれない詳細は今後ブログに書いていくつもり Twitter で @shutooike をフォローして、読んで、拡散して、感想ください(欲張 り)
自動テスト編
実行順序のランダム化 ファイル名順での実行になっており順番に依存したテストがちらほら spec ファイルを追加すると突然落ちるようになったり config.order = :random テスト数がそこまで多くなかったのでエイヤで :random にして、ローカルやCIで落ち
たら都度修正 Kernel.srand config.seed とすると Array#sample も seed で再現するようになる
テストデータが残らないように CIで特定のテストが失敗することが稀によくある 他のテストで作ったデータが原因 example ごとにデータを消す config.use_transactional_fixtures = true before(:all) で作りっぱなしのデータ
before(:each) に変更 after(:all) で削除
自動テストを書いていく(1) 日々の開発で自分が関わったコードからテストを書いていく なぜか書きにくいという実感が... 原因突き詰めたところ →「そうか!土台が出来てないんだ!」 プロジェクト発足
実際の最初の issue
スキップしていたモデルのコールバックを解放 User.skip_callback(:create, :after, :foo) 思考停止でのほぼ全てのコールバックがスキップされていた プロダクトコードではデータ作成時にコールバックで行っている処理を、テストデ ータ作成時は自分で明示的に処理しないといけない状況 テスト環境の挙動は出来る限り本番環境に近づけるべき 一部残したコールバックもある 時間がかかりすぎるもの
外部サービスを呼び出すもの モック化するという手もある
FactoryBot のフル活用 お馴染みのテストデータ生成ライブラリ シナリオに沿ったテストデータをサクッと作れないとテストを書くハードルが一気に上 がる 導入はされているが、上手く運用はされていない状況 FactoryBot の定義方針を検討・決定 詳しくはブログに書きます 方針に沿って
FactoryBot を再定義・既存のテストを修正 最初は頑張って全モデルでやろうとしたがその必要はなかった よく出てくるスタメンモデルだけでOK
自動テスト戦略を立てる ここまでテストを書いた感覚でテスティングピラミッドを目指すのは厳しいと判断 単体テストを継続的に書くのは開発者の力量に依存するところが大きい 良くない単体テストはむしろ開発生産性を下げる プライベートメソッドのテストを書く 期待値生成にコードと同じロジックを用いる... など まずは統合テストたくさん書いていくことにする 目指すはテスティングトロフィー 主な
Rails の責務である API の request spec を書いていく とりあえずある程度までカバレッジを上げたい
誰でも書ける request spec helper を用意 誰でも書けるように薄さにこだわる 正常系のみ 最低限の条件 ログインユーザー パラメーター
最低限の検証 APIの権限チェック HTTPステータスコードチェック APIを作ったら絶対書こうねという意識付け →「お約束」というネーミング
利用側のイメージ 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 どのように実装したかはブログに書きます
全APIに正常系テスト(お約束)を書いていく 全体カバレッジが60%ほどに APIのカバレッジは90%近くに 少なくとも最低限の正常系の動作が変わっていないことが担保できるようになったので 安心感が全然違う コスパ最強
request spec でAPIの振る舞いを検証する クリティカルユーザージャーニー(CUJ)を定義 ユーザーが 1 つの目的を達成するために行うサービスとの一連のインタラクション CUJをもとにサービスの主要な操作に関するAPIに対して正常系・異常系の振る舞いを検 証 振る舞いとは?
データのCRUD ジョブのキック 外部サービスの呼び出し... など 主要なAPIの動作の担保され安心感がさらに上がった
最低限のE2Eテストを書く SPAといいつつ複数画面がある メイン画面 編集画面 外部連携用画面... など 最低限の検証 画面が正常に開けることのみ確認 ステージング環境でのドックフーディングでは普段開かない画面がひっそり死んでるこ とに気づけるように
バージョンアップ編
Ruby 2.3 → 2.7.6 警告の対応 依存 gem のバージョンアップ 開発環境でバージョンアップ 自動テスト・手動テスト
他の開発者の開発環境に展開 ステージング環境バージョンアップ 手動テスト 本番環境バージョンアップ
Rails 5 → 6 警告の対応 依存 gem のバージョンアップ 開発環境でバージョンアップ rails
app:update new_framework_defaults 自動テスト・手動テスト ステージング環境バージョンアップ 手動テスト 本番環境バージョンアップ
リファクタリング編
複数モデルで使える共通の処理を concern に Rails がデフォルトで用意している仕組み 上手く使えたら綺麗に抽象化できる 上手く使えたら... 例 created_by, updated_by
を入れる処理
単体テストが書きやすくなるような実装に 自動テスト戦略で統合テストに注力したもう1つの理由として単体テストが書きにくい実 装になっていた ドメインロジックがコントローラー層に Fat Controller より単体テストが書きやすいようにドメインロジックをモデルに寄せていく必要がある Fat Model 自動テストがある程度書けたので振る舞いが変わらないことを担保しながらリファクタ
リングができる
[再掲] 課題 テストが継続的に書かれてない 簡単な不具合修正、機能改善、負債返却で他が壊れることがある 言語・ライブラリのバージョンアップが継続的に行われていない EOL切れ バージョンアップ時の防衛線がステージング環境にしかいない 同じようなロジックがあちこちに点在
得られたもの テストが書きやすい環境 APIを作成したらお約束テストを追加する文化 ある程度のカバレッジ コードの変更やライブラリのバージョンアップなどに対する心理的安全性 他に影響があるコード変更がCIで落ちる ライブラリのバージョンアップの非互換に気付けたことも
[再掲] 課題 テストが継続的に書かれてない 簡単な不具合修正、機能改善、負債返却で他が壊れることがある 言語・ライブラリのバージョンアップが継続的に行われていない EOL切れ バージョンアップ時の防衛線がステージング環境にしかいない → 継続してやっていく 同じようなロジックがあちこちに点在
→ 継続してやっていく
これから とりあえず「EOLを切らない」を目標に Ruby, Rails を上げていく dependabot をフル活用してライブラリアップデートを習慣化 テスティングピラミッドを目指す 単体テストが書きやすくなるよう引き続きリファクタリング ステージング環境で行っている手動テストの負荷低減
現在の最低限のE2Eから少し検証範囲を広げる
まとめ 自動テストは書かないと上手くならない 勘所を掴むまでクソテストコードを書いて書いて書きまくれ 幸いテストコードは書き直しやすい レガシーコード改善に銀の弾丸はない カバレッジも可読性、メンテナビリティも突然上がることはない 理想に向けてロードマップを引き地道にやっていくしかない レガシーコードを愛する ユーザーの課題を解決し、売上を立てるレガシーコードは偉大 なりたくてレガシーコードになった訳じゃない
その時々の最適解だったはず 恨みではなくリスペクトを持って接する
おまけ1 カバレッジは正しく測ろう 以下の2つを行ったらカバレッジがグッと上がった # config/envioroments/test.rb config.eager_load = true spring を使わずに
RSpec を実行 原因ははっきりわかっていないがおそらくコードの分母が正しく測れるようになったか ら 知ってる人いたら教えてください
おまけ2 カバレッジを追い求めすぎない カバレッジが高いからバグが出ない訳じゃない 効果的なテストをすることが一番重要 カバレッジは目標にせず、指標にするべき
None
None
None