こんにちは、とらのあなラボのはっとりです。
今回は、開発環境で使用しているRuboCopの設定を標準化したプロセスについてお話しします。
RuboCopの重要性
RuboCopは、Ruby言語で書かれたコードを静的解析することで、一貫性のあるコーディングスタイルを促進し、潜在的なバグを指摘するツールです。開発チームが成長するにつれて、コードベースも複雑化し、多くの開発者が関与するようになります。このとき、一貫したコーディング標準がなければ、コードの可読性や保守性が大きく低下するリスクがあります。RuboCopは、このような問題を事前に防ぎ、効率的でバグの少ない開発プロセスを支援します。
https://github.com/rubocop/rubocop
プロダクト間でのRuboCop設定のばらつきとその問題点
とらのあなラボでは、多くのRailsプロダクトを手掛けていますが、プロダクトごとにRuboCopの設定が異なっていました。これが開発者にとって以下のような問題を引き起こしていました
- バージョンの不一致
- 開発が異なる時期に始まるため、RuboCopのバージョンが異なり、一貫したコーディングスタイルの適用が困難でした。
- 過去の設定の継承
- 新しいプロダクトで既存のプロダクトの設定をそのまま利用することがあり、最新のベストプラクティスが反映されていないことが多くありました。
- コードスタイルのばらつき
- 各プロダクトでRuboCopの設定が統一されていないため、コードの書き方にバリエーションが生じていました。
- 新しいルールの採用遅れ
- 特に古いプロダクトには新しいRuboCopのルールが反映されておらず、コード品質の向上が阻害されていました。
これらの問題を解消するために、全プロダクトで一貫したRuboCopの設定を導入することが必要とされました。
具体的な設定の紹介
RuboCopの設定を標準化するにあたり、以下の点を考慮しました
- Gemのバージョン
- 全プロダクトで同じバージョンのRuboCopを使用するようにしました
- プロダクトごとのカスタマイズ
- 標準を決めつつ、プロダクトごとにカスタマイズできるようにしました
- カスタマイズできる範囲についても定めました
- 既存を考慮した設定
- 既存の修正が大量に発生しやすいものは今回は見送りました(将来的に少しずつ厳しくしていく予定)
- 自動修正が可能なルールであれば既存の修正が多くても有効にしました
- 非Rails用設定
- 例えばLambdaなど Ruby を使っているがRailsを使っていないプロダクトもあるので非Rails用設定も用意しました
- 今回のブログでは紹介しないのですが、Rails用の設定からRailsのルールを除いただけのものになります
具体的な設定手順を以下に示します
設定手順
Gemfileに対して以下を追加する ※gemバージョンについては定期的に更新予定
group :development, :test do gem 'rubocop', '~> 1.63.0', require: false gem 'rubocop-performance', '~> 1.21.0', require: false # Railsでない場合は以下1行削除 gem 'rubocop-rails', '~> 2.24.1', require: false gem 'rubocop-rake', '~> 0.6.0', require: false end
追加したら bundle install
する
次に、以下の3ファイルを用意する
- .rubocop.yml
- プロダクト独自ルールを入れる場合はここに入れる。
- .rubocop_base.yml
- とらのあなラボ共通設定。定期的に最新を取得すること。
- .rubocop_todo.yml
- 標準設定導入にあたって、一時的に無効化するルール。
.rubocop_todo.yml は初期は空ファイルとして用意する
.rubocop_base.ymlの内容一部抜粋(全体はリンクから参照してください。)
require: - rubocop-performance - rubocop-rails - rubocop-rake AllCops: NewCops: enable SuggestExtensions: true Rails: Enabled: true inherit_mode: merge: - Exclude # クラスあたりの長さを制限 Metrics/ClassLength: Max: 120 CountAsOne: - array - hash - heredoc - method_call # #### 中略 #### # 以降はRubyの専用記法を強制するルールだが、あえて書き直すほどではないため無効化 Style/NumericPredicate: Enabled: false Style/SymbolArray: Enabled: false Style/WordArray: Enabled: false
.rubocop.ymlの内容一部抜粋(全体はリンクから参照してください。)
# TODO: 利用しているライブラリがあればコメントアウトを外して追加する # 必要なければコメントごと削除する # require: # - rubocop-capybara # - rubocop-factory_bot # - rubocop-rspec # - rubocop-rspec_rails AllCops: # TODO: 利用しているRubyバージョンを追加する TargetRubyVersion: 3.2 # TODO: 利用しているRailsバージョンを追加する TargetRailsVersion: 6.1 # #### 中略 #### # TODO: Metricsで必要な設定があればコメントアウトを外して値を変更する。 # ただし、許容値以上にはしないこと # 必要がない設定はコメントごと削除する # # クラスあたりの長さを制限 # Metrics/ClassLength: # Max: 120 # 200まで許容 # #### 中略 ####
.rubocop.yml は TODO項目を対応した上でプロダクトに配置する
設置したら以下のコマンドで .rubocop_todo.yml を生成する
bundle exec rubocop --auto-gen-config --exclude-limit 99999 --no-auto-gen-timestamp --auto-gen-only-exclude
これにより既存のRuboCopの警告は出なくなる。※後々、.rubocop_todo.yml
の量を減らす対応は必要になる
具体的な改修ルールの紹介
.rubocop_todo.yml
で無効化したものが大量にあるのでそれを減らすためのルールや手順も定めました。
- 大量の修正になりそうであればプルリクエストを分ける
- レビュワーの負担緩和のため
- その際は機能改修は入れないようにする
.rubocop_base.yml
は定期的に更新するので、定期的にプロダクトへ反映する必要がある- 自動修正は積極的に使う
rubocop -a
で安全に修正できる自動修正である旨をプルリクエストに記載する。rubocop -A
で修正した場合は、動作やパフォーマンスが変わるものもあるので必ず手動での動作確認もする
- 何らかの理由でルールの変更や無効化をする場合はなるべくアーキテクトチームに相談の上で行う
当初はRuboCopの修正と機能改修は必ず別にするルールにする予定でしたが、既存の問題修正を減らしやすくするため、大量でない限りは機能改修には混ぜてもよいルールにしました。
既存の警告へ対応するための工夫
.rubocop_todo.yml
で既存の問題点を除外するようにしましたが、このままだと除外内容が放置されやすくなります。
そこで、コミット対象のファイルに抑制したRuboCop警告があれば表示する pre-commitのスクリプトを作りました。
※ChatGPTにほとんど作ってもらいました。
#!/bin/sh # RuboCop実行関数 run_rubocop() { bundle exec rubocop } # ステージングされている Ruby ファイルのリストを取得 files=$(git diff --cached --name-only --diff-filter=ACM | grep '\.rb$') if [ -z "$files" ]; then exit 0 fi # `.rubocop_todo.yml` のバックアップを一時ディレクトリに作成 cp .rubocop_todo.yml /tmp/.rubocop_todo.yml.bak # `.rubocop_todo.yml` を一時的に空にする > .rubocop_todo.yml # RuboCop を実行し、出力を変数に格納 output=$(run_rubocop "$files") status=$? # `.rubocop_todo.yml` を元に戻す mv /tmp/.rubocop_todo.yml.bak .rubocop_todo.yml # 終了ステータスが 0 以外の場合は、エラーがあるため出力を表示 if [ $status -ne 0 ]; then echo "======================================================================" echo "RuboCop warnings:" echo "$output" echo "\033[33m コミット対象ファイルの中にRuboCopの警告があります。可能であれば修正してください。 \033[0m" echo "\033[33m 修正後は以下のコマンドを実行してください。 \033[0m" echo "\033[33m bundle exec rubocop --auto-gen-config --exclude-limit 99999 --no-auto-gen-timestamp --auto-gen-only-exclude \033[0m" echo "======================================================================" fi # コミットを常に続行 exit 0
コミット対象ファイルにRuboCop警告があれば以下のような表示になります。
内容確認して、もし余裕があればついでに直してもらいます。
除外したRuboCop警告に気付いてもらうためのものであるためコミットの阻害まではしません。
フィードバックと今後の展望
新しいルールを適用した後、各チームからのフィードバックを受けます。このフィードバックを基に、ルールや設定の微調整を行っています。各開発チームがより効率的に、より快適で安全にコードを書ける環境を目指しています。
まとめ
この標準化プロジェクトを通じて、チーム全体のコードの一貫性が向上し、新しいメンバーがプロジェクトに参加しやすくなることを期待しています。また、RuboCopの新しいルールを随時評価し、適宜設定を更新していくことで、技術的負債の軽減とコード品質の維持向上を図っていきます。
採用情報
虎の穴ラボでは一緒に働く仲間を募集中です!
この記事を読んで、興味を持っていただけた方はぜひ弊社の採用情報をご覧ください。
toranoana-lab.co.jp