自分の足を撃たない技術

  • 35
    いいね
  • 0
    コメント

Battle Conference U30 での登壇資料です。


自己紹介


だれこれ

  • Masataka Kuwabara(pocke)
  • こないだ23歳になりました
  • Actcat Inc. Engineer
  • Ruby / RuboCop
    • 今日は Ruby での例が多くなります :gem:
  • Twitter: @p_ck_
  • GitHub: @pocke

minpocke.png


普段やってること


SideCI

https://sideci.com

自動コードレビュー

2017-03-08-213138_1198x616_scrot.png
170311124738.png


Shibart

https://shibart.pocke.me

GitHub の芝を Tシャツにする

1487946652.png
t-shirt.png


本題


今日のテーマ

  • 挑戦
    • 私が挑戦していること
    • かつ、皆さんにも挑戦していただきたいこと

私は何に挑戦しているのか

表題は「自分の足を撃たない技術」

  • 自分の足を2度撃たない技術
  • バグを再発させないこと

に挑戦


突然ですが、バグを直す時何をしていますか?


バグを直す時の、よくある光景

  • バグを直す
  • 直っていることを確認するテストを書く
  • (余力があれば)注意喚起の為にブログなどで記事を書く

これはバグの再発防止に繋がっているのか?

  • テストを書く
    • 該当箇所の正しさは保証される
    • ただし、他の関数内 / 他のプロダクトではまた同じバグを生むかも知れない
  • 記事を書く
    • 危ないコード例を広く知らせることが出来る
    • ただし、人間は忘れる生き物
      • また同じ様なバグを産んでしまわないとは限らない

これらの対策のみでは不十分!


そこで Lint !


Lint とは

  • プログラミング言語の静的解析ツール
  • コンパイラ / インタプリタだけではチェックできない、バグの原因となるコードをチェックする

たとえば Ruby での例


バグのあるコード

Ruby では Python や算数みたいに 0 < x < 20 のように書くことが出来ない

# A Ruby program
x = 3

# NoMethodError undefined method `<' for true:TrueClass
if 0 < x < 20
  puts x
end
$ ruby tset.rb
test.rb:5:in `<main>': undefined method `<' for true:TrueClass (NoMethodError)

RuboCop を使うと

RuboCop は Ruby 用の Lint

$ rubocop
Inspecting 1 file
W

Offenses:

test.rb:5:4: W: Use the && operator to compare multiple values.
if 0 < x < 20
   ^^^^^^^^^^

1 file inspected, 1 offenses detected

0 < x < 20 ではなく 0 < x && x < 20 を使用するように指摘してくれる!


Lint のメリット

  • テストと違い、全てのコードに対して適用可能
    • テスト: テスト対象にしか効果がない
    • Lint: テスト対象のコード以外にも、他のプロダクトのコードにも適用可能
  • ブログを使用して情報発信した場合と違い、機械的に検査できる
    • ブログ: 人間が覚えてないと意味がない
    • Lint: 機械が検査するので忘れない!(CIを設定するのがオススメ)

ここまでは Lint を使う話


Lint を使う から 作る へ

  • Lint を使うとバグを事前に防ぐことが出来る
    • テスト等とは違いとても広い範囲に有効
  • それならば、Lint のルールをみんなで書けばみんなハッピーなのでは!

作ってしまったバグをどんどん Lint のルールにしていこう!


RuboCop のルール(Cop)を書くのは意外と簡単

さっきの 0 < x < 20 を検出する RuboCop のルールの実装は結構短い

def_node_matcher :multiple_compare?, <<-PATTERN
  (send (send _ {:< :> :<= :>=} $_) {:< :> :<= :>=} _)
PATTERN

def on_send(node)
  return unless multiple_compare?(node)

  add_offense(node, :expression)
end

https://github.com/bbatsov/rubocop/blob/master/lib/rubocop/cop/lint/multiple_compare.rb


実装するための情報も豊富

日本語の情報も充実しています

何かあったら Twitter で @p_ck_ に聞けば :ok_woman:


Feature Request の Issue を作るのもアリ

  • 「こういうルールがほしい」「作ろう!:thumbsup:」のような流れもよくある
  • 気兼ねなく Issue を建てよう
    • 全世界の他のプログラマが同じバグを作らないため

でも RuboCop だけじゃうまくいかない時も…

  • バグの原因が社内特有で公開するものではない
    • 社内で定義されているメソッド起因とか
  • RuboCop に Issue / PR 出すにはハードル高い

そこで Querly!

Ruby 用の Lint ツール

soutaro/querly

  • ルールを書くのが簡単
    • YAML で定義出来る
  • 使う人がルールを定義出来る
    • ローカルルールについて検査出来る。
      • RuboCop には適さない項目も検査できる!

Querly の例

例えば社内では以下のようなルールを書いています(一部抜粋)。

- id: com.sideci.oj
  pattern:
    - JSON.load
    - JSON.dump
  message: Oj を使ってください
- id: com.sideci.shallow_dig
  pattern: "dig(_)"
  before: "task.dig('id')"
  after: "task['id']"
  message: 階層が2以上でなければ、Hash#dig(_)よりもHash#[]を使ってください。

Ruby 以外の言語の場合

選択肢は沢山ある


まとめ

  • バグを再発させない為に、Lint を整える挑戦をしよう :muscle:
    • 同じ過ちを繰り返して(自分の|世界中のプログラマの)時間を無駄にしない為に
  • Lint を整えるのは難しくない
    • Ruby なら RuboCopQuerly がオススメ
    • 他の言語でもよく使われているものを見てみよう

ご清聴ありがとうございました。