チームのstyleguideを作ろう! #3

Product Develop Division Renosyチームの渡邉(@evitch)です。半年前にGA technologiesに転職し、現在はRailsエンジニアとしてRENOSYプロダクトのWebフォーム開発を担当しています。Ruby歴はまだ3ヶ月です。

先週(チームのstyleguideを作ろう! #2 - GA technologies Tech Blog)に続き、Renosyチームで勉強会を実施しましたので、その内容をアウトプットしていきたいと思います。

Renosyチームでstyleguideを作っている目的を再掲します。

テーマ: チーム内のstyleguideを作ろう!

Renosyチームは、結成当初は3人のエンジニア体制でしたが、現在10名まで増え、そのバックボーンも新卒からベテラン中途まで様々です。 そのため、コードの品質やチーム開発のためにstyleguideを作った方がいいなと感じることが増えてきました。 しかし、白紙な状態からつくるのも難しいため、まず最初はCookpadさんのstyleguideを参考にさせていただこうと考えました。 そこで、勉強会の時間を使って、Cookpadさんのstyleguideを読みあって、議論を深め、自分たちのstyleguideへエンハンスしていきたいと考えています。

github.com

今週取り扱ったテーマは以下の4つです。

  • メソッド呼び出し
  • BEGIN と END
  • モジュールとクラスの定義
  • メソッドの定義

メソッド呼び出し

RubyMineなどのエディタを利用している方は、コミットする前にエディタの機能でコード整形をかけることがほとんどだと思います。うちのチームでは、ほぼ全てのメンバーがRubyMineを利用しているため、RubyMineにあるコードフォーマット機能の初期設定に準拠したスタイルに統一することで、リファクタリングにかけるコストを低く抑えようという狙いがあります。RubyMine先生大好き。

以下、決定事項を列挙します。

  • 中括弧によるブロックを1行で書く場合は、{}とブロックパラメータおよび本体コードの間に空白を入れてはならない
# good (RubyMine style)
[1, 2, 3].each {|num| puts num}

# bad
[1, 2, 3].each { |num| puts num }
  • メソッド呼び出しにおいて、1行の文字数が120を超える場合、は以下の規則に基いて複数行に分けて書くこと

  • メソッド呼び出しの開き括弧 ( の直後で改行し、次の行からインデントレベルを下げて実引数を1行に1つずつ書き、メソッド呼び出しの閉じ括弧 ) を独立した行にインデントレベルを戻して書くこと

Foo.new(
  arg,
  long_argument,
  key: value,
  long_key: long_value,
  pretty_so_much_very_long_key:
    pretty_so_much_very_tooooooooooooooooooooo_long_value
)

BEGINとEND

私はこのBEGINとENDを知りませんでした。Rubyのリファレンスマニュアルによると、以下のような処理をするようです。

  • BEGIN

    初期化ルーチンを登録します。BEGINブロックで指定した文は当該ファ イルのどの文が実行されるより前に実行されます。複数のBEGINが指定 された場合には指定された順に実行されます。

  • END

    「後始末」ルーチンを登録します。END ブロックで指定した文はインタ プリタが終了する時に実行されます。

docs.ruby-lang.org

これはなかなか強力ですね・・・。 副作用、影響範囲を把握しきれない複数人開発の場合だと、想定外の挙動をして怖いなーという印象を受けました。 ライブラリ開発とかに手を出すのであれば是非使ってみたいですね。

以下、決定事項を列挙します。

  • BEGIN ブロックと END ブロックは使ってはならない

モジュールとクラスの定義

そんなに盛り上がらなかったテーマでした。Coockpadさんのスタイルをそのまま取り入れました。

YARD の略し方は面白いなと思いました(Yay! A Ruby Documentation Tool)。それがありだったらなんでもありですよね。Yay!

以下、決定事項を列挙します。

  • メソッドのエイリアスを定義する場合は表記揺れを防止するため alias ではなく alias_method を使用する

  • アクセサを定義する場合は attr_accessor、attr_reader、attr_writer を使用し、attr は使用しない

  • クラスメソッドの定義では、無闇にインデントレベルを深くしないために self. を使用すること。但し、privateなものとpublicなものを同時に定義したい場合等は、class << selfを利用しても構わない

class Foo
  # good
  def self.foo
  end

  # bad
  class << self
    def foo
    end
  end
end
  • private や protected なクラスメソッドを定義する場合は class << self/end の中でメソッドを定義し、可視性を変更すること
class Foo
   # good
   class << self
     def foo
     end
     private :foo
   end

   # bad
   def self.foo
   end
   class <<self
     private :foo
   end
 end
  • メソッド定義の後で、そのメソッドの可視性を変更するために private や protected や public を引数付きで呼び出す場合は、メソッド定義とこれらのメソッド呼び出しの間に空行を入れてはならない
class Foo
  # good
  def foo
  end
  private :foo

  # bad
  def foo
  end

  private :foo
end
  • private や protected や public を引数なしで使用する場合、インデントレベルはメソッド定義と同じレベルとし、前後に1行ずつ空白を入れること
# good
class Foo
  def foo
  end

  private

  def bar
  end
end

# bad
class Foo
  def foo
  end

private

  def bar
  end
end

# bad
class Foo
  def foo
  end

  private

    def bar
    end
end

# bad
class Foo
  def foo
  end

  private
  def bar
  end
end
  • モジュールやクラスが外部に公開するメソッドとアクセサに対してドキュメンテーションコメントを Markdown 形式で書くこと

  • ドキュメンテーションコメントはYARDのフォーマットを推奨

  • ドキュメンテーションコメントとメソッド定義の間に空行を書いてはならない

メソッドの定義

以下、決定事項を列挙します。

  • メソッド定義において、引数リストの括弧は省略してはならない。

  • 引数なしメソッドを定義する場合は括弧を省略すること。

  • メソッドと引数リストの括弧の間に空白を置いてはならない。

  • メソッドの本体コードにコメントを書かなければ理解できないようなコードを書かないようにすること。

    • メソッド本体内にコメントを書くよりも、別のメソッドに分けて適切な名前を付ける方が可読性が向上する。
    • ただし、数式に対する補足や出典などはコード本体中にコメントとして書いても良い。
  • 引数に破壊的変更を加えてはならない

# bad
def a2z(str)
  str.gsub!("a", "Z")
end

tmp = "aaaaa"
puts a2z(tmp)
puts tmp

感想

チームのスタイルガイドを作ることを目的として勉強会を開催していますが、副次的効果としてメンバ間のコミュニケーション活性に一役買っている感じがします。こういう風にみんなでワイワイできる勉強会は楽しいですね。自分たちが書いたものじゃないから意見が言いやすいってのもあると思います。