1200行あったRuboCopのTodoを半年かけて(ほぼ)解消した話
こんにちは、バックエンドエンジニアの山口拓弥(@yamat47)です。
ここ最近は オクトパストラベラー というちょっと昔のゲームに思いっきりハマっていて、気付いたら100時間以上もやりこんでいました。
しかしそれもついにお盆で完結してしまって、なんだか心にぽっかり穴が空いた日々を過ごしています...。
さて今回は、くらそうね開発チームでのRuboCopに関する改善活動の様子をお伝えします。
RuboCopの導入以来ずっと放置してしまっていた .rubocop_todo.yml
をこの半年で(ほぼ)全て解消したのですが、そのために取り組んできたことをご紹介します!
.rubocop_todo.yml
のおさらい
RuboCopと RuboCop とはいわゆるRubyのリンターです。
一度CIに設定しておけば「インデントは揃えてね」といった瑣末なコードレビューをせずとも最低限の質が保たれていくため、Rubyを使っているプロジェクトなら大抵の場合は導入されているのではないでしょうか。
RuboCopの便利さの紹介は今回の主題ではないので割愛するとして...
RuboCopを導入・運用していく上で大切なファイルの一つが .rubocop_todo.yml
です。
.rubocop_todo.yml
とはRuboCopにおける設定ファイルの一つで、現状のコードが違反してしまっているルール(cop)を無効にすることができます。
ただしあくまでも「Todo」なので、ルール自体を利用しないというよりは「後で直すよ」というニュアンスを含んだ無効の設定です。
RuboCopを導入するタイミングでいきなり全てのルール違反に対応するのはとても大変です。
gem install rubocop
をしたらまず最初に rubocop --auto-gen-config
をして、現状の違反は全て無視する = 今後のファイル変更だけチェックさせるように設定することも多そうです。
くらそうね開発チームとRuboCopの出会い
くらそうね開発チームでは2019年12月20日にRuboCopを導入しました。
rails new
をしたのがその年の5月8日でしたので、半年以上経ってから使い始めたことになります。
私が入社する前の話なので当時の詳しい経緯はわからないことも多いのですが...
多くの例に漏れず導入直後に .rubocop_todo.yml
を自動生成しつつ、それ以降に修正されるコードに対してチェックしていく方針で運用をしていました。
自動生成した時点の .rubocop_todo.yml
は 1239行 ありました。
そこから約一年ほど、Todoが解消されることはなくむしろ少しずつ増えていってしまう日々が続いていました...。
どうやってRuboCopのTodoを解消していったの?
放置されること幾年月、昨年の年末に一念発起してRuboCopのTodoを本格的に解消し始めました。
そこから半年ほどかけてTodoを解消してきましたが、その間の .rubocop_todo.yml
の行数の推移はこんな感じでした:
以降、Todoの解消のために具体的に行ったことを紹介していきます。
Todoを解消するプルリクエストが定期的に自動で作られる仕組みを作った
「怠慢な」チームであるために、まず考えるのは自動化でした。
RuboCop ChallengerというライブラリをGitHub Actionsと組み合わせて、残っているTodoのうちの一つを解消するプルリクエストが毎週月曜日に自動で作られる仕組みを作りました。
ドキュメントがしっかりしているので実装で困ることはなさそうですが、GitHub Actionsでの仕組みづくりの勘所は別の記事にまとめています。
よければ参考にしてください。
先ほどの行数推移の画像の一部の期間では減っていく割合が大体一定でしたが、これはこの自動化の仕組みに乗って解消し続けていた表れです。
人が行わなければならない活動をなるべく小さくすることで、無理なく続けることができました。
RuboCop Challengerを導入したプルリクエストも紹介しておきます:
他にも「RuboCop Challengeとは?」という内容のドキュメントをつくり、チーム内での啓蒙も行いました。
この活動はコードを介してチームメイト全員に強い影響があるものなので、どうやったら自分ごとにしてもらえるか(最低限関心を持ってもらえるか)という視点での仕組み作りが目的でした。
それでも緊急対応が続いて業務が逼迫していた期間は全く対応できず、プルリクエストが放置されて続けていました。
ここまで整えてもそんな状況だったので、手動での削除を続けていたらどうなっていたことやら...
また慣れてきたらプルリクエストを自動で作る頻度を増やしました。
これも最初に仕組みを丁寧に使ったことで、GitHub Actionsの設定ファイルを少し書き換えるだけで対応することができました。
結局は時間をとって手動で一気に解消した
RuboCop Challengerで解消できるのは RuboCopの自動修正機能 が有効なルールに限られます。
いくつかのルールは自動修正に対応しておらず、それらは人が手動で解消する必要がありました。
それであれば...ということで、仕事の余裕ができたタイミングで一気に手動で解消をしました。
ルールを一つずつ確認しつつ、次の手順でコード全体を確認していきました。
rubocop --auto-gen-config
をして設定ファイル群を最新の状態にする。- 一つルールを選んでルールを無効にするかTodoを解消するか判断する。それに従って
.rubocop.yml
か.rubocop_todo.yml
を編集する。 rubocop -A
をして(自動修正されることを期待しつつ)違反している部分を確認する。- 自動修正されなかった箇所のコードを確認して違反しない形に直す。
- 差分を確認して
git commit
。
この中だと「一つルールを選んでルールを永久に無効にするかTodoを解消するか判断する。」というステップが一番大切です。
公式のドキュメント にもありますが、Todoは全てを解消しなければならないわけではありません。
そもそもそのルール自体がチームのスタイルに合わない場合はルール自体を無効にしましょう。
自動修正されるプルリクエストをレビューするときも、この視点は欠かさないようにしました(実際にルール自体を無効にしたこともありました)。
RuboCop Challengerのおかげで違反しているファイル数が少ないものが残っていたので、一つ一つの解消にはそこまで時間はかかりませんでした。
当時のコミットの履歴はこんな感じです:
行数の推移の画像での最後の期間は傾きがやや不規則でしたが、手動での解消に取り組んでいたのがその理由です。
自動で解消できるものがなくなって以降はRuboCop Challengerの仕組みが不要になったので、最後は削除してしまいました。
ありがとう、RuboCop Challenger...!!
最終的に、1239行あったTodoを19行まで減らせました!
こうした活動の結果、半年かけてTodoを19行にまで減らせました!
自動修正を活用したところも多くありましたが、65分の1にまでTodoを減らせたということで一定の成果を挙げられたかなと思っています。
半年間の活動の様子を行数の推移と照らし合わせるとこんな感じです:
ちなみに、唯一残してしまっているTodoは Naming/MemoizedInstanceVariableName というルールについてのものです。
もちろん機械的に対応することはできますが、特にコントローラーでの変数名の変更の影響が読みづらく二の足を踏んでしまっています。
自動テストがもうちょっと充実してきたら、CIの結果を踏まえて自信を持って対応するつもりです!
おわりに
今回はくらそうね開発チームにおけるRuboCopのTodoの解消への取り組みについてお伝えしました。
もちろんTodoの解消自体は目的ではありません。
プロジェクトによって様々な考え方があるとは思いますが、私たちの場合は「RuboCopの設定の棚卸し」がその大きな理由でした。
どのルールを有効にしていてどのファイルがTodoとして残っているのかわかりづらい状態だったのを整理できたので、半年間取り組んできた成果はそこそこあったのかなと感じています。
クラッソーネでは(RuboCopに限らず)開発環境改善やCIの改善に興味があるエンジニアを大募集中です。
興味を持っていただけた方、ぜひ私と一度カジュアル面談でお話ししましょう!