Posted at

自分の足を撃たない技術

More than 1 year has passed since last update.

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 がオススメ

    • 他の言語でもよく使われているものを見てみよう



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