Slide 1

Slide 1 text

omakaseしないための .rubocop.yml のつくりかた 2024/10/26 Kaigi on Rails 2024 Shu Oogawara(@expajp) 1

Slide 2

Slide 2 text

• Shu Oogawara(@expajp ) • Engineering Manager@リンカーズ • Engineering Manager Meetup コアスタッフ • 趣味 • 秘境駅めぐり • 深夜ラジオ • 筋トレ 2 自己紹介

Slide 3

Slide 3 text

みなさん、RuboCop使ってますか? 3 画像引用:“rubocop/rubocop: A Ruby static code analyzer and formatter, based on the community Ruby style guide.”, https://github.com/rubocop/rubocop, 2024/10/06閲覧

Slide 4

Slide 4 text

• Ruby向けの静的コードアナライザ・フォーマッタ • 個別のルールを”Cop” と呼ぶ • .rubocop.yml でCopごとの有効/無効やオプション設定が可能 4 コードにルールを設け、それを強制するツール

Slide 5

Slide 5 text

• 危険を避ける • 不具合を予防する • ベストプラクティスを強制する • コードベースの保守性を高める • 一貫性を保証する • 無用な議論を避ける 5 なぜコードにルールを設けるのか 画像引用:オライリー・ジャパン, ” Googleのソフトウェアエンジニアリング”, https://www.oreilly.co.jp/books/9784873119656/, 2024/10/06閲覧

Slide 6

Slide 6 text

# bad open(something) open("| #{something}") open("| foo") URI.open(something) # good File.open(something) IO.popen(something) URI.parse(something).open # good (literal strings) open("foo.text") URI.open("http://example.com") • コマンドインジェクション 脆弱性を防ぐ • Kernel#open に変数などを 渡していたら検出する 6 目的別のCopの例 危険を避ける:Security/Open 引用:”Security :: RuboCop Docs”, https://docs.rubocop.org/rubocop/cops_security.html#securityopen, 2024/10/21閲覧

Slide 7

Slide 7 text

• *(アスタリスク)が 乗算か配列展開か ひと目で分からない 書き方を検出する 7 目的別のCopの例 ベストプラクティスを強制:Lint/AmbiguousOperator # bad # (中略) do_something *some_array # good # (中略) do_something(*some_array) 引用:”Lint :: RuboCop Docs”, https://docs.rubocop.org/rubocop/cops_lint.html#lintambiguousoperator, 2024/10/21閲覧

Slide 8

Slide 8 text

• {}(波括弧)内側の スペースの有無を検出 • ありなしどちらを正とするかは 設定次第 8 目的別のCopの例 一貫性を保証:Layout/SpaceInsideBlockBraces # bad some_array.each {puts e} # good some_array.each { puts e } 引用:”Layout :: RuboCop Docs”, https://docs.rubocop.org/rubocop/cops_layout.html#layoutspaceinsideblockbraces, 2024/10/21閲覧

Slide 9

Slide 9 text

コードにルールを設けることは利益がある 9

Slide 10

Slide 10 text

10 しかし、既存のコードベースにRuboCopを導入するのは大変 大量の警告 コンフリクト地獄 サードパーティ プリセットいっぱいありすぎ 僕は気にしないんで 勝手にやってください 時間を使いたくない 好みの問題でしょ 圧倒的物量 開発遅れるなら やめてもらえますか そんなことより ここの実装なんですけど これを無効に するなんて ありえない いくつに設定するのが 正しいんでしょうねえ 自転車置き場

Slide 11

Slide 11 text

11 しかし、既存のコードベースにRuboCopを導入するのは大変 大量の警告 コンフリクト地獄 サードパーティ プリセットいっぱいありすぎ 僕は気にしないんで 勝手にやってください 時間を使いたくない 好みの問題でしょ 圧倒的物量 開発遅れるなら やめてもらえますか そんなことより ここの実装なんですけど これを無効に するなんて ありえない いくつに設定するのが 正しいんでしょうねえ 自転車置き場 設定が先延ばしにされがち

Slide 12

Slide 12 text

12 最近の話題 Rails 7.2 から RuboCopがデフォルトで導入されるようになった RuboCopの設定にもレールを敷いてくれるのでは?

Slide 13

Slide 13 text

13 Issue 画像引用:” Add (a very basic!!) Rubocop by default · Issue #50456 · rails/rails · GitHub”, https://github.com/rails/rails/issues/50456, 2024/10/06閲覧

Slide 14

Slide 14 text

14 Blog 画像引用:”A writer's Ruby”, https://world.hey.com/dhh/a-writer-s-ruby-2050b634, 2024/10/06閲覧

Slide 15

Slide 15 text

RuboCopは”自分たちの”スタイルを一貫させる ツールとして素晴らしい スタイルをまだ見つけてないチームのため omakaseのメニューを用意したので出発点にしてほしい 15 Blogの要約(発表者による) 画像引用:” dhh (David Heinemeier Hansson)”, https://github.com/dhh, 2024/10/06閲覧

Slide 16

Slide 16 text

• rubocop-rails-omakase • 7.2以降で `rails new` するとデフォルトで入るgem • 有効に設定されているCopはわずか46個 • 参考:拡張なしRuboCopに含まれるCopは554個(RuboCop 1.67) • Layout: 27 / Style: 12 / Lint: 3 / Performance: 2 / Rails: 2 • 他のCopはすべて無効になっている 16 出発点としてのomakase ほぼ「一貫性を保証する」役割しか担っていない

Slide 17

Slide 17 text

omakaseは銀の弾丸にあらず やっぱり自分たちのスタイルを見つけなくてはならない 17

Slide 18

Slide 18 text

omakaseは銀の弾丸にあらず やっぱり自分たちのスタイルを見つけなくてはならない 18 前述の通り、簡単には見つけられない

Slide 19

Slide 19 text

omakaseは銀の弾丸にあらず やっぱり自分たちのスタイルを見つけなくてはならない 19 しかし、これを話し合って決めるのは簡単ではない 一方で

Slide 20

Slide 20 text

我々のチームは9ヶ月かけて話し合い 自分たちの.rubocop.yml をつくることに成功した 20

Slide 21

Slide 21 text

当初の.rubocop_todo.yml 21 • 501行 • 59Copを無視 • 6275箇所の違反

Slide 22

Slide 22 text

22 9ヶ月後

Slide 23

Slide 23 text

23 今日の話 既存のコードベースにRuboCopを導入したとき 設定をどう決めていくか?

Slide 24

Slide 24 text

• .rubocop.yml をつくる難しさを知り克服する • .rubocop.yml のつくりかたを実践する • 成功の要因を深堀りする 24 Agenda

Slide 25

Slide 25 text

.rubocop.yml を つくる難しさを知り克服する

Slide 26

Slide 26 text

Q. そもそも.rubocop.yml をつくるのはなぜ難しいか? 26

Slide 27

Slide 27 text

• 直接は売上を生まないから • チームの文化を言語化・集約したものだから 27 Q. そもそも.rubocop.yml をつくるのはなぜ難しいか?

Slide 28

Slide 28 text

• 直接は売上を生まないから • チームの文化を言語化・集約したものだから 28 Q. そもそも.rubocop.yml をつくるのはなぜ難しいか?

Slide 29

Slide 29 text

29 文化 人間の生活様式の全体。(中略) それぞれの民族・地域・社会に固有の文化があり、 学習によって伝習されるとともに、 相互の交流によって発展してきた。 “ “文化(ブンカ)とは? 意味や使い方 - コトバンク”, https://kotobank.jp/word/%E6%96%87%E5%8C%96-128305, 2024/10/22閲覧

Slide 30

Slide 30 text

RuboCopは”自分たちの”スタイルを一貫させる ツールとして素晴らしい スタイルをまだ見つけてないチームのため omakaseのメニューを用意したので出発点にしてほしい 30 Blogの要約(発表者による) 画像引用:” dhh (David Heinemeier Hansson)”, https://github.com/dhh, 2024/10/06閲覧 再掲

Slide 31

Slide 31 text

RuboCopは”自分たちの”スタイルを一貫させる ツールとして素晴らしい スタイルをまだ見つけてないチームのため omakaseのメニューを用意したので出発点にしてほしい 31 Blogの要約(発表者による) 画像引用:” dhh (David Heinemeier Hansson)”, https://github.com/dhh, 2024/10/06閲覧 再掲 スタイル≒生活様式≒文化

Slide 32

Slide 32 text

• 直接は売上を生まないから • チームの文化を言語化・集約したものだから 32 Q. そもそも.rubocop.yml をつくるのはなぜ難しいか? 生活様式を言語化し チームで統一する作業が 簡単なはずはない

Slide 33

Slide 33 text

• 売上を生まない 33 Q. .rubocop.yml をつくるのは何が難しいか? • 文化の言語化・集約

Slide 34

Slide 34 text

• 売上を生まない 34 Q. .rubocop.yml をつくるのは何が難しいか? • 文化の言語化・集約 時間の確保が難しい

Slide 35

Slide 35 text

• 売上を生まない 35 Q. .rubocop.yml をつくるのは何が難しいか? • 文化の言語化・集約 時間の確保が難しい ファシリテーション が難しい

Slide 36

Slide 36 text

• 売上を生まない 36 Q. .rubocop.yml をつくるのは何が難しいか? • 文化の言語化・集約 時間の確保が難しい ファシリテーション が難しい これらを克服する必要がある

Slide 37

Slide 37 text

• 売上を生まない 37 Q. .rubocop.yml をつくるのは何が難しいか? • 文化の言語化・集約 時間の確保が難しい ファシリテーション が難しい

Slide 38

Slide 38 text

• 売上を生まない 38 Q. .rubocop.yml をつくるのは何が難しいか? • 文化の言語化・集約 時間の確保が難しい ファシリテーション が難しい

Slide 39

Slide 39 text

39 どこで時間を取るか? すでに定期開催されているMTGの中

Slide 40

Slide 40 text

40 定期MTGの中に時間を確保する なぜ時間の確保が難しいか? •売上を生む開発を妨げるから

Slide 41

Slide 41 text

41 定期MTGの中に時間を確保する なぜ時間の確保が難しいか? •売上を生む開発を妨げるから 逆に、開発を妨げなければ時間を取って良い

Slide 42

Slide 42 text

• 一度にたくさん取らず、少しずつ取る • スケジュールが想定する可処分時間を圧迫しない • 他の会議と連続して取る • 開発メンバーが集中しているのを中断しない 42 定期MTGの中に時間を確保する 開発を妨げない時間の取り方

Slide 43

Slide 43 text

• 一度にたくさん取らず、少しずつ取る • スケジュールが想定する可処分時間を圧迫しない • 他の会議と連続して取る • 開発メンバーが集中しているのを中断しない 43 定期MTGの中に時間を確保する 開発を妨げない時間の取り方 両方を満たすのが「定期MTGの中」

Slide 44

Slide 44 text

• 売上を生まない 44 Q. .rubocop.yml をつくるのは何が難しいか? • 文化の言語化・集約 時間の確保が難しい ファシリテーション が難しい

Slide 45

Slide 45 text

• 売上を生まない 45 Q. .rubocop.yml をつくるのは何が難しいか? • 文化の言語化・集約 時間の確保が難しい ファシリテーション が難しい

Slide 46

Slide 46 text

• 意見が割れやすい • 参加の足並みを揃えづらい • オプション・適用範囲の選択肢が多すぎる 46 ファシリテーションが難しい RuboCop設定について話し合う難しさ

Slide 47

Slide 47 text

47 ファシリテーションが難しい RuboCop設定について話し合う難しさ 一人ひとりの文化の違いを尊重しつつ、 設定をひとつに決めなくてはならない

Slide 48

Slide 48 text

48 ファシリテーションが難しい RuboCop設定について話し合う難しさ 一人ひとりの文化の違いを尊重しつつ、 設定をひとつに決めなくてはならない 意見を尊重するため、表明の幅は広く ひとつに決めるため、採決ルールは細かく

Slide 49

Slide 49 text

49 ファシリテーションが難しい 表明の幅は広く・採決ルールは細かく

Slide 50

Slide 50 text

• 意見が割れやすい 50 ファシリテーションが難しい 表明の幅は広く・採決ルールは細かく 意見が割れたときの ルールを細かく設定

Slide 51

Slide 51 text

• 意見が割れやすい 51 ファシリテーションが難しい 表明の幅は広く・採決ルールは細かく 意見が割れたときの ルールを細かく設定 • 参加の足並みを揃えづらい 「気にしない」を表明可に

Slide 52

Slide 52 text

• 意見が割れやすい 52 ファシリテーションが難しい 表明の幅は広く・採決ルールは細かく 意見が割れたときの ルールを細かく設定 • 参加の足並みを揃えづらい • オプション・適用範囲の 選択肢が多すぎる 「気にしない」を表明可に デフォルトに寄せつつ 希望のオプションを表明可に

Slide 53

Slide 53 text

• 時間の確保が難しい • 定期MTGの中に時間を確保する • ファシリテーションが難しい • 意見表明の幅は広く、採決ルールは細かく 53 まとめ:.rubocop.yml をつくる難しさを知り克服する

Slide 54

Slide 54 text

.rubocop.yml の つくりかたを実践する

Slide 55

Slide 55 text

ここから、我々のチームがどういうフローで 話し合いを進めていったかを説明していく 55

Slide 56

Slide 56 text

• デフォルトをベースに.rubocop_todo.yml を減らしていく 56 基本戦略 引用:Koichi ITO, ” Beyond the RuboCop Defaults - Speaker Deck”, https://speakerdeck.com/koic/beyond-the-rubocop-defaults, p.36, 2024/10/24閲覧

Slide 57

Slide 57 text

57 基本的な流れ ①準備をする ②意見を形成する ③意見を表明する ④意見を集約する ⑤コードを修正する ⑥継続する

Slide 58

Slide 58 text

• .rubocop_todo.yml の上から2個Copを選ぶ • 週次MTGで使う議事録(事前に自動作成) にCopの説明を書いておく 58 手順①:準備を行う

Slide 59

Slide 59 text

• MTGがはじまったら、メンバーは説明を読む • その後コードのどの部分に指摘が入るか見ながら確認する 59 手順②:意見を形成する

Slide 60

Slide 60 text

• Slackで投票を行う • 投票は / / / の4択 • :デフォルト設定で有効 • :どちらでもよい • :無効 • :オプション変更して有効 60 手順③:意見を表明する

Slide 61

Slide 61 text

• 基本的には多数決で決める 61 手順④:意見を集約する デフォルト 設定で有効

Slide 62

Slide 62 text

• 基本的には多数決で決める 62 手順④:意見を集約する デフォルト 設定で有効 は投票の総数に入れず 特に話も振らない

Slide 63

Slide 63 text

• まずは各々に理由を尋ね、投票先を変える時間を設ける 63 手順④:意見を集約する 意見が割れたら

Slide 64

Slide 64 text

• 多数派と少数派になったら • 了承をとって多数派の意見を採用 64 手順④:意見を集約する 意見が割れたら デフォルト 設定で有効

Slide 65

Slide 65 text

• 真っ二つに割れたままなら • 自由に書けた方が良いとみなして無効にする 65 手順④:意見を集約する 意見が割れたら 無効 ※あまり良い感じの実例がなかったです

Slide 66

Slide 66 text

• どう設定したいかと理由を尋ねる • その場で選択肢を作り再投票 • それでも割れたら ファシリテーターがジャッジ 66 手順④:意見を集約する に票が入った/設定の希望が出たら

Slide 67

Slide 67 text

• 回り持ちでPR作成の担当者をアサインする • 担当者をラウンドロビンで指名するSlack Appを作って利用 67 手順⑤:コードを修正する

Slide 68

Slide 68 text

• 毎週のチームでの所要時間は15分くらい • これを淡々と続けていく • 週次MTGの時間に余裕がない場合は、無理せずスキップして翌週へ 68 手順⑥:継続する

Slide 69

Slide 69 text

69 そして時は流れ、9ヶ月後……

Slide 70

Slide 70 text

我々は自分たちの.rubocop.yml を 作り上げることができた 70

Slide 71

Slide 71 text

成功の要因を深堀りする

Slide 72

Slide 72 text

72 なぜ、やり切ることができたのか? 要因①:オーナーシップ

Slide 73

Slide 73 text

• 確実に開催されるように準備の担当を自分に固定した • 継続されないのが最大のリスクと捉えたため • メンバー間で温度差はあって当たり前と考え、しくみを作った • 「協力してもらう」スタンスで頑張る人を減らすことが 結局は設定を作り切る近道 73 なぜ、やり切ることができたのか? 要因①:オーナーシップ

Slide 74

Slide 74 text

74 なぜ、やり切ることができたのか? 要因②:チーム文化の下支え

Slide 75

Slide 75 text

• テストを当たり前に書く文化 • 検証コストを最小にするために十分なテストスイートは必須 • 心理的安全性の高い文化 • スタイルの議論はどうしても個々人の好みがぶつかりがち • 反対意見を気軽に表明・議論し、ルールに従って決めてノーサイド、 という「大人の対応」が求められる 75 なぜ、やり切ることができたのか? 要因②:チーム文化の下支え

Slide 76

Slide 76 text

まとめ: .rubocop.yml のつくりかた

Slide 77

Slide 77 text

77 まとめ:.rubocop.yml のつくりかた 定期MTGで少しずつ話し合う 意見表明の幅は広く、採決ルールは細かく オーナーシップを持って話し合いをリードする テストと心理的に安全な文化が下支えする

Slide 78

Slide 78 text

ここまで、.rubocop.yml のつくりかたを説明してきた しかし、一度作り切ることはゴールではない 78

Slide 79

Slide 79 text

.rubocop.yml とはチーム文化の反映 79

Slide 80

Slide 80 text

チームの文化は必ず移り変わる ならば.rubocop.yml も移り変わらなくてはならない 80

Slide 81

Slide 81 text

「定期MTGに組み込む」方法は継続的なメンテへの動線にもなる 81

Slide 82

Slide 82 text

.rubocop.yml を一度作り切ったあとは みなさんの手で文化と共に育てていきましょう 82

Slide 83

Slide 83 text

文化はomakaseできない 自分たちの手で育てて .rubocop.yml に写し取ろう Fin.