pockestrap

Web Programmer's memo

デバッグのためにコメントアウトして死ぬのをやめたい

タイトルのようなバグと戦うためのツールを作成した。 この記事はその宣伝である。

TL;DR

github.com

バグとの出会い

先日、おもしろバグに出会った。 Rails を使用しているプロジェクトで、本来必要なbefore_actionコメントアウトされていた。

# こんなかんじ
class ExampleController < ApplicationController
  # before_action :validate_foobar
end

この状態でも正常ケースではアプリケーションは正しく動作する。 ただし、異常ケースにおいてはbefore_actionで弾かれるべき入力値でもそのまま処理が進んでしまう様になっていた。

コミットを追ってみると、このコメントアウトがなされたのはだいぶ昔のことだとわかった。 そのため原因は定かではないが、私は「デバッグの為にバリデーションをコメントアウトしたが、コミットする時に元に戻すのを忘れた」のが原因ではないかと睨んでいる。

バグを避けるには?

このバグを避けるにはどうしたらいいだろうか。

悪い例

まず、ありきたりな再発防止策として「人間がコードレビューを頑張る」というものが考えられるが、無意味だ。
確かにこのバグはコードレビューの際に見つけられるべきではあった。しかし、このようなバグが発生する度にコードレビューを頑張るようにしていては、無限にレビュー項目が増えていくだろう。
そしてまたレビューの際にバグを見落とす。

コードによる解決

そのため、私はこのような問題に対してはコードを書くことで解決しようと考えている。

まず最初に、デバッグ用のコメントアウト専用の Syntax を追加することを考えた。 デバッグ用のコメントには専用の Syntax を使用し、production にはそのような Syntax が入らないようにする。

例えば以下のような Syntax が考えられる

# コメントのシャープの数を増やす
class ExampleController < ApplicationController
  ### before_action :validate_foobar
end

# セミコロンを使う。セミコロンは普通の Ruby プログラムであまり使われないため、これに意味をもたせる。
# `require`や`load`を上書きすればこれをコメントに置き換えることは容易だろう。
class ExampleController < ApplicationController
  ;; before_action :validate_foobar
end

# セミコロンで囲む
class ExampleController < ApplicationController
  ;before_action :validate_foobar;
end

この Syntax を使うには2つのツールを提供する必要がある。

  1. 上記 Syntax を解釈するライブラリを作る。
    • シャープの数を増やす場合は必要ない
    • 前述の通りrequireなどをオーバーライドすることになるだろう
  2. 上記 Syntax が production に入る前に弾くツールを作る。
    • シャープが3つ続いていたら警告する、等
    • セミコロンの場合 RuboCop の Style/Semicolon で代用可能
    • 雑に grep でもよいだろう

問題点

ところがこの方式について検討しているうちに、あまりよいデザインではない様に感じてきた。

  • シャープ1つによるコメントの方が、専用 Syntax より楽
    • 人間は面倒くさがりであるため、面倒な専用 Syntax を使わずに通常のコメントを使う
    • 通常のコメントを使われた場合、この方式は何も防げない
  • requireの上書きは影響範囲が予測できないためあまりやりたくない
  • ツールを2つ提供する必要がある。

真の解決策

今までの方式には問題があることがわかったため、結果として別の方式を採用した。

この方式では Ruby コードとして解釈可能な文字列がコメントアウトされていた場合に、それを警告するツールを提供する。

# This is a cool controller.
class ExampleController < ApplicationController
  # before_action :validate_foobar
end

例えば上記のコードが与えられた場合、このツールは3行目のコメントに対して警告を出すが、1行目のコメントは Ruby プログラムとして正しくないため警告を出さない。

このツールを Pull-Request に対して適用することで、コメントアウトされている Ruby コードを「それはデバッグ用のコメントアウトなのではないか」と自動でレビューすることが可能である。

Installation

このツールは DontComment という名前で作成されている。

github.com

また、 RubyGems として作成されているため、gemコマンドでインストールすることが可能である。

$ gem install dont_comment

効能

このツールは、実は2つの効能を持ち合わせている。

偽陽性

このツールはかなりの偽陽性のある警告を出す。

コメントアウトされている文字列が Ruby コードとして解釈できる」ものであれば警告を出すため、人間には明らかにドキュメントとしてのコメントだと感じられるものに対しても警告を出してしまう。 例えば、http://example.comRuby コードとして解釈することが可能であるが、人間からしたら URL に見えるだろう。

そのため、このツールを使用する上ではある程度の偽陽性を許容する。
このツールをレポジトリのコード全体に適用するのはナンセンスであり、前述の通り Pull-Request に対して適用するのが現実的であろう。
またその場合も偽陽性のある警告が出た際に無視できる仕組みがあると良いだろう。

ただし、ある程度の偽陽性を防ぐ取り組みを行うことは考えている。
例えば現状でも ASCII 以外の文字が多くを占める文字列は Ruby コードではないと判定している。
# このメソッドは String を返すといったコメントの文字列部分は Ruby コードとしても解釈可能であるが、これが Ruby コードであることを意図して書かれていることはかなり少ないだろう。

この他にもいくつかの防止策を考えているが、まだ未実装だ。

まとめ

このツールを使用することで、冒頭に上げたバグを防ぐことが可能になると考えている。
まだ実運用にはのせられていない為、今後実用化に向けた取り組みを行っていきたい。