No Purpose

If I must say, it's for me.

Rubocopを導入しない世界ですべき振る舞い

このエントリで書いてないこと

  • Rubocopというgem名について

このエントリを読んでその点について議論したくなるかもしれないけど、筆者が書きたいわけではないこと

  • プロジェクトにRubocopを導入するかどうか

このエントリで書くこと

このツイートに至った経緯と、その上での自分の考えを書く。

現職での前提

基本的にRubocopを採用しないプロジェクトが多い。 理由はいくつかあるんだろうけれど、基本的にはこういう話なのかなって思っている。

あるいは「Rubocopが指摘するような事柄の中には画一的に決められるものだけでなく、都度どのような選択をとるべきか考えることに心血を注ぐべきだから」といった考え方があったという話も耳に挟んだ。

これらのスタンスに対する自分の意見としては、

  • これらの考え方は、基本的には自転車置き場の議論になりやすいCop、特にStyleやLayoutに対するものが多いと思っている
  • それらのCopを全てdisableにしたとしても、Rubocopで得られるものはそれなりにあるのではないか(SecurityやLintのCopなど)
  • ただし「じゃあどのCopをdisableにして、どのCopをenableにするのか」のメンテや、メンバーの考え方のすり合わせにコストがかかるのは理解できるので、一律で導入しない選択も全否定しない
    • 特にOwnershipがあいまいになりがちな共通ライブラリであれば、関わる人数の多さからそのコストが大きくなってしまうことは想像に易い

前職では厳し目のconfigによるRubocopに馴らされていたこともあり、導入したくないメンバーが多い現職に戸惑っていたのも正直な本音としてあるのだが、それでも導入するコスト/リターンと、導入しないそれとを勘案したときに、現状の結論に至っていることについての一定の理解はあったつもりだ。 少なくとも、現状の採用していないプロジェクトに後から導入するコストは大きいだろうなと思っていた。(rubocop_todo.ymlみたいなテクニカルな解決ができる話というよりは、メンバーの心理的な話が大きい)

というか、前職ではRubocopの推奨するStyleに異論唱えがちだったのは僕だったんだよな。

本題: 今日あったこと

Railsで書かれたコードのちょっとした修正をするのに Time.current という書き方をした。 ActiveSupport::TimeWithZone を利用した現在時刻の取得だが、この書き方についてはほぼ手癖だったと言っていい。

このPRにおいて、同僚から Time.zone.now に変更するCommit Suggestionをもらった。

自分の理解ではこれらのメソッドは等価だと思っていたので、単なるStyleの違いをどちらかに揃えるよう指摘されたのか聞き返した。

...いや、もっと正直に言います。 「Rubocopを導入しない」ということは「Styleの違いを許容する」だと思っていたので「そういうことの指摘はしないでほしい」っていう態度を示しました。ごめんなさい。

それに対する同僚からのレスとして、自分が入社する前に書かれた「時間に関する緩やかな指針」という社内ドキュメントを提示してもらった。曰く、

  • Time.current は使わないようにした方がいい
  • Time.currentは内部で「 Time.zone がなければ Time.now を使う」という挙動をとる
  • Railsアプリケーションを書いている限り間違いは怒らないが、時間ほど重要なものであれば、念を入れるに越したことはない

といったことが書いてあった。

参考: https://github.com/rails/rails/blob/6a033a06855f0b7a407ff8edbfc9f9aa45527edc/activesupport/lib/active_support/core_ext/time/calculations.rb#L38-L40

このことについて僕は知らなかったので、できる限り Time.zone.now を使うべきという指針は一理あるように思えた。(とはいえ、明らかにRailsってわかってるならどっちでもよいんじゃないかなって気持ちも、正直ちょっとはある)

ただ、New Joinerが目につきにくい場所に"指針"があるのは望ましくないように思えるし、今後は自分も含めて Time.current って使われてるの見たら都度コメントしなきゃいけないのかな?みたいなことが頭をよぎって、当初は素直に受け入れにくかった。

あるいは「だったらRubocop導入すればいいんじゃないかな」って気持ちになったのも事実なんだけど、冒頭書いたように、このエントリで「Rubocopを導入すべきか」を議論したいわけではない。

今思えば、これは「Rubocop導入しないことで発生したコスト」なので、「このコストを払うくらいなら、Rubocop導入するコストを払ったほうがいい!」「なんなら、俺が頑張ってそのコストを引き受けるから導入しよう!」って言うんだったら誠実だよなーと思っている。

はい、そのとおりですよね...↓

で、このツイートに至る

たとえば、僕はなんとなく今回の件は「きっとCopでなんとかなるだろうな」っていう感覚があったから、それならRubocop使えばいいのではって考え方に走るのだが、指摘をしてくれた同僚は知らなかった。

参考までに、rubocop-railsのこのCopで対応できる。

https://docs.rubocop.org/rubocop-rails/cops_rails.html#railstimezone

もちろん「知ってるべき」という考えはあんまりなくて、どっちかというと Time.current の内部挙動について知ってた方がうれしい気がする。

...というわけで結局、ここで長々と書いているのは

「Rubocopを導入しない」ということは「Styleの違いを許容する」だと思っていたので「そういうことは指摘しないでほしい」っていう態度を示しました。

についての反省文なのかもしれない。

じゃあRubocopを導入しない世界ですべき振る舞いとは?

自戒をこめて言語化してみる。

  • 前提として、実利のない(と各々が信じる)議論は避ける
  • その上で、レビュアーやレビューイに「Rubocopがどんなことを指摘できるか」の知識を求めない
  • 結果として、自分がレビューされるときはRubocopのような指摘をもらうこともあるし、自分がレビューするときはRubocopがしてくれるような指摘をすることもある
  • 同じような指摘が何度か続いて、それらのコストとリターンが釣り合わないと思ったら、Rubocop導入を提言する
  • 別途コードガイドラインにしておきたい事項があるなら見えやすいところに置いておく

というわけで、チームにそれなりの習熟度を必要とするんだけど、そこまで無理な世界だとは思わない。

あと、このあたりについて他の人の考えを知れたらうれしいという気持ちもある。

ちなみに

社内の他の言語のプロジェクトではLinter(Formatter)が使われることが多い。 js/tsのプロジェクトではESLint+Prettier、Goのプロジェクトではもちろんgofmt、Elixrプロジェクトではmix formatというものが使われているらしい。SwiftとKotlinはどうしてるんだろう。

あと、Rubocopとは別のアプローチとして、Dangerってツールがあることを知った。(これは社内で使われている)

danger.systems