Syntax

  • Use :: only to reference constants(this includes classes and modules). Do not use :: for regular method invocation. [link]

    # bad
    SomeClass::some_method
    some_object::some_method
    
    # good
    SomeClass.some_method
    some_object.some_method
    SomeModule::SomeClass::SOME_CONST
    
  • Use def with parentheses when there are parameters. Omit the parentheses when the method doesn't accept any parameters. [link]

     # bad
     def some_method()
       # body omitted
     end
    
     # good
     def some_method
       # body omitted
     end
    
     # bad
     def some_method_with_parameters param1, param2
       # body omitted
     end
    
     # good
     def some_method_with_parameters(param1, param2)
       # body omitted
     end
    
  • Define optional arguments at the end of the list of arguments. Results can be surprising when calling methods that have optional arguments at the front of the list. [link]

    # bad
    def some_method(a = 1, b = 2, c, d)
      puts "#{a}, #{b}, #{c}, #{d}"
    end
    
    some_method("w", "x") # => "1, 2, w, x"
    some_method("w", "x", "y") # => "w, 2, x, y"
    some_method("w", "x", "y", "z") # => "w, x, y, z"
    
    # good
    def some_method(c, d, a = 1, b = 2)
      puts "#{a}, #{b}, #{c}, #{d}"
    end
    
    some_method("w", "x") # => "w, x, 1, 2"
    some_method("w", "x", "y") # => "w, x, y, 2"
    some_method("w", "x", "y", "z") # => "w, x, y, z"
    
  • Avoid the use of parallel assignment for defining variables. Parallel assignment is allowed when it is the return of a method call, used with the splat operator, when used to swap variable assignment, or for creating obviously related data clumps. Parallel assignment is less readable than separate assignment. [link]

    # bad
    a, b, c, d = "foo", "bar", "baz", "foobar"
    
    # good
    a = "foo"
    b = "bar"
    c = "baz"
    d = "foobar"
    
    # good:  swapping variable assignment
    # Swapping variable assignment is a special case because it will allow you to
    # swap the values that are assigned to each variable.
    a = "foo"
    b = "bar"
    
    a, b = b, a
    puts a # => "bar"
    puts b # => "foo"
    
    # good:  method return
    def multi_return
      [1, 2]
    end
    
    first, second = multi_return
    
    # good:  use with splat
    first, *list = [1, 2, 3, 4]
    
    hello_array = *"Hello"
    
    a = *(1..3)
    
    # good:  data clump
    x, y = 4, 2
    
  • Do not use for. Iterators should be used instead. for is implemented in terms of each (so you're adding a level of indirection), but with a twist: for doesn't introduce a new scope (unlike each) and variables defined in its block will be visible outside it. [link]

    arr = [1, 2, 3]
    
    # bad
    for elem in arr do
      puts elem
    end
    
    # note that elem is accessible outside of the for loop
    elem # => 3
    
    # good
    arr.each { |elem| puts elem }
    
    # elem is not accessible outside each's block
    elem # => NameError: undefined local variable or method `elem'
    
  • Do not use then for multi-line if/unless. [link]

    # bad
    if some_condition then
      # body omitted
    end
    
    # good
    if some_condition
      # body omitted
    end
    
  • Always put the condition on the same line as the if/unless in a multi-line conditional. [link]

    # bad
    if
      some_condition
      do_something
      do_something_else
    end
    
    # good
    if some_condition
      do_something
      do_something_else
    end
    
  • Favor the ternary operator(?:) over if/then/else/end constructs. It's more common and obviously more concise. [link]

    # bad
    result = if some_condition then something else something_else end
    
    # good
    result = some_condition ? something : something_else
    
  • Use one expression per branch in a ternary operator. This also means that ternary operators must not be nested. Prefer if/else constructs in these cases. [link]

    # bad
    some_condition ? (nested_condition ? nested_something : nested_something_else) : something_else
    
    # good
    if some_condition
      nested_condition ? nested_something : nested_something_else
    else
      something_else
    end
    
  • Do not use if x; .... Use the ternary operator instead. [link]

    # bad
    result = if some_condition; something else something_else end
    
    # good
    result = some_condition ? something : something_else
    
  • Leverage the fact that if and case are expressions which return a result. [link]

    # bad
    if condition
      result = x
    else
      result = y
    end
    
    # good
    result =
      if condition
        x
      else
        y
      end
    
  • Use when x then ... for one-line cases. The alternative syntax when x: ... has been removed as of Ruby 1.9. [link]

  • Do not use when x; .... See the previous rule. [link]

  • Use ! instead of not for boolean negation. [link]

    # bad:  parentheses are required because of op precedence
    x = (not something)
    
    # good
    x = !something
    
  • The and, or, and not keywords are for flow control, not conditional comparisons like &&, ||, and !. They have a different operator precedence to support this usage. [link]

    # bad:  conditional comparisons
    if some_condition and not some_other_condition
      do_something
    end
    
    # bad:  flow control
    # same as: size = (ARGV.shift || abort "USAGE:  #{$PROGRAM_NAME} SIZE")
    size = ARGV.shift || abort "USAGE:  #{$PROGRAM_NAME} SIZE"
    
    # good:  conditional comparisons
    if some_condition && !some_other_condition
      do_something
    end
    
    # good:  flow control
    # same as: (size = ARGV.shift) or (abort "USAGE:  #{$PROGRAM_NAME} SIZE")
    size = ARGV.shift or abort "USAGE:  #{$PROGRAM_NAME} SIZE"
    
  • Avoid multi-line ?: (the ternary operator); use if/unless instead. [link]

  • Favor modifier if/unless usage when you have a single-line body. Another good alternative is the usage of flow control and/or/not. [link]

    # bad
    if some_condition
      do_something
    end
    
    # good
    do_something if some_condition
    
    # another good option
    some_condition and do_something
    
  • Avoid modifier if/unless usage at the end of a non-trivial multi-line block. [link]

    # bad
    10.times do
      # multi-line body omitted
    end if some_condition
    
    # good
    if some_condition
      10.times do
        # multi-line body omitted
      end
    end
    
  • Avoid nested modifier if/unless/while/until usage. Favor &&/|| if appropriate. [link]

    # bad
    do_something if other_condition if some_condition
    
    # good
    do_something if some_condition && other_condition
    
  • Favor unless over if !... for negative conditions (or flow control or). [link]

    # bad
    do_something if !some_condition
    
    # bad
    do_something if not some_condition
    
    # good
    do_something unless some_condition
    
    # another good option
    some_condition or do_something
    
  • Do not use unless with else. Rewrite these with the positive case first. [link]

    # bad
    unless success?
      puts "failure"
    else
      puts "success"
    end
    
    # good
    if success?
      puts "success"
    else
      puts "failure"
    end
    
  • Don't use unless with compound conditionals. [link]

    # bad
    unless question? || other_question?
      puts "confusing"
    end
    
    # good
    if !question? && !other_question?
      puts "easier to grok"
    end
    
    # good
    unless question?
      puts "simple"
    end
    
  • Don't bury side effects in compound conditionals. [link]

    # bad
    if cool_dude && user.save && wearing_sunglasses
      # ...
    end
    
    # good:  explicit change
    if cool_dude
      user_saved = user.save
      if user_saved && wearing_sunglasses
        # ...
      end
    end
    
    # good:  simple conditional
    if user.save
      # ...
    end
    
  • Don't use parentheses around the condition of an if/unless/while/until. [link]

    # bad
    if (x > 10)
      # body omitted
    end
    
    # good
    if x > 10
      # body omitted
    end
    

Note that there is an exception to this rule, namely safe assignment in condition.

  • Do not use while/until ... do for multi-line while/until. [link]

    # bad
    while x > 5 do
      # body omitted
    end
    
    until x > 5 do
      # body omitted
    end
    
    # good
    while x > 5
      # body omitted
    end
    
    until x > 5
      # body omitted
    end
    
  • Favor modifier while/until usage when you have a single-line body. [link]

    # bad
    while some_condition
      do_something
    end
    
    # good
    do_something while some_condition
    
  • Favor until over while !... for negative conditions. [link]

    # bad
    do_something while !some_condition
    
    # good
    do_something until some_condition
    
  • Use Kernel#loop instead of while/until when you need an infinite loop. [link]

      # bad
      while true
        do_something
      end
    
      until false
        do_something
      end
    
      # good
      loop do
        do_something
      end
    
  • Use Kernel#loop with break rather than begin/end/until or begin/end/while for post-loop tests. [link]

    # bad
    begin
      puts val
      val += 1
    end while val < 0
    
    # good
    loop do
      puts val
      val += 1
      break unless val < 0
    end
    
  • Omit parentheses around parameters for methods that are part of an internal DSL (e.g. Rake, Rails, RSpec), methods that have "keyword" status in Ruby (e.g. attr_reader, puts) and attribute access methods. Use parentheses around the arguments of all other method invocations. [link]

    class Person
      attr_reader :name, :age
    
      # omitted
    end
    
    temperance = Person.new("Temperance", 30)
    temperance.name
    
    puts temperance.age
    
    x = Math.sin(y)
    array.delete(e)
    
    bowling.score.should == 0
    
  • Omit the outer braces around an implicit options hash. [link]

    # bad
    user.set({name: "John", age: 45, permissions: { read: true }})
    
    # good
    user.set(name: "John", age: 45, permissions: { read: true })
    
  • Omit both the outer braces and parentheses for methods that are part of an internal DSL. [link]

    class Person < ActiveRecord::Base
      # bad
      validates(:name, {presence: true, length: { within: 1..10 }})
    
      # good
      validates :name, presence: true, length: { within: 1..10 }
    end
    
  • Omit parentheses for method calls with no arguments. [link]

    # bad
    Kernel.exit!()
    2.even?()
    fork()
    "test".upcase()
    
    # good
    Kernel.exit!
    2.even?
    fork
    "test".upcase
    
  • Use the proc invocation shorthand when the invoked method is the only operation of a block. [link]

    # bad
    names.map { |name| name.upcase }
    
    # good
    names.map(&:upcase)
    
  • Use {...} when defining a block where the return value matters and do...end when using a block for flow control or side effects. Many guides recommend {...} for single-line blocks and do...end for multiple lines, but you can already tell that at a glance and it's helpful to signal the intended usage instead. That said, single-line do...end is awkward, so do split those across multiple lines, even if it's short. [link]

    names = %w[bozhidar steve sarah]
    
    # bad:  block return value matters
    names.map do |name|
      name.capitalize
    end
    
    # good:  map() always uses {...} because the return value always matters
    names.map { |name| name.capitalize }
    
    # bad:  block return value is ignored; used for side effects (printing)
    names.each { |name| puts name.capitalize }
    
    # passable:  awkward
    names.each do |name| puts name.capitalize end
    
    # good:  each() always uses do...end because the return value never matters
    names.each do |name|
      puts name.capitalize
    end
    
    # bad:  block return value matters
    headers = File.open("names.csv") do |f|
      f.gets
    end
    
    # bad:  block used for flow control
    File.open("sorted_names.txt") { |f|
      f.each_cons(2) do |first, last|
        puts "#{first.strip} comes before #{last.strip}"
      end
    }
    
    # good:  {...} and do...end clarify intent of open() usage
    headers = File.open("names.csv") { |f| f.gets }
    
    File.open("sorted_names.txt") do |f|
      f.each_cons(2) do |first, last|
        puts "#{first.strip} comes before #{last.strip}"
      end
    end
    
  • Consider using explicit block argument to avoid writing block literal that just passes its arguments to another block. Beware of the performance impact, though, as the block gets converted to a Proc. [link]

    require "tempfile"
    
    # bad
    def with_tmp_dir
      Dir.mktmpdir do |tmp_dir|
        Dir.chdir(tmp_dir) { |dir| yield dir }  # block just passes arguments
      end
    end
    
    # good
    def with_tmp_dir(&block)
      Dir.mktmpdir do |tmp_dir|
        Dir.chdir(tmp_dir, &block)
      end
    end
    
    with_tmp_dir do |dir|
      puts "dir is accessible as a parameter and pwd is set: #{dir}"
    end
    
  • Avoid return where not required for flow control. [link]

    # bad
    def some_method(some_arr)
      return some_arr.size
    end
    
    # good
    def some_method(some_arr)
      some_arr.size
    end
    
  • Avoid self where not required. (It is only required when calling a self write accessor.) [link]

    # bad
    def ready?
      if self.last_reviewed_at > self.last_updated_at
        self.worker.update(self.content, self.options)
        self.status = :in_progress
      end
      self.status == :verified
    end
    
    # good
    def ready?
      if last_reviewed_at > last_updated_at
        worker.update(content, options)
        self.status = :in_progress
      end
      status == :verified
    end
    
  • As a corollary, avoid shadowing methods with local variables unless they are both equivalent. [link]

    class Foo
      attr_accessor :options
    
      # ok
      def initialize(options)
        self.options = options
        # both options and self.options are equivalent here
      end
    
      # bad
      def do_something(options = {})
        unless options[:when] == :later
          output(self.options[:message])
        end
      end
    
      # good
      def do_something(params = {})
        unless params[:when] == :later
          output(options[:message])
        end
      end
    end
    
  • Don't use the return value of = (an assignment) in conditional expressions unless the assignment is wrapped in parentheses. This is a fairly popular idiom among Rubyists that's sometimes referred to as safe assignment in condition. [link]

    # bad
    if v = array.grep(/foo/)
      do_something(v)
      ...
    end
    
    # good
    if (v = array.grep(/foo/))
      do_something(v)
      ...
    end
    
    # passable
    v = array.grep(/foo/)
    if v
      do_something(v)
      ...
    end
    
  • Use shorthand self assignment operators whenever applicable. [link]

    # bad
    x = x + y
    x = x * y
    x = x**y
    x = x / y
    x = x || y
    x = x && y
    
    # good
    x += y
    x *= y
    x **= y
    x /= y
    x ||= y
    x &&= y
    
  • Use ||= to initialize variables only if they're not already initialized. [link]

    # bad
    name = name ? name : "Bozhidar"
    
    # bad
    name = "Bozhidar" unless name
    
    # good:  set name to "Bozhidar", only if it's nil or false
    name ||= "Bozhidar"
    
  • Don't use ||= to initialize boolean variables. (Consider what would happen if the current value happened to be false.) [link]

    # bad:  would set enabled to true even if it was false
    enabled ||= true
    
    # good
    enabled = true if enabled.nil?
    
  • Use &&= to preprocess variables that may or may not exist. Using &&= will change the value only if it exists, removing the need to check its existence with if. [link]

    # bad
    if something
      something = something.downcase
    end
    
    # bad
    something = something ? something.downcase : nil
    
    # ok
    something = something.downcase if something
    
    # passable
    something = something && something.downcase
    
    # good
    something &&= something.downcase
    
  • Avoid explicit use of the case equality operator ===. As its name implies it is meant to be used implicitly by case expressions and outside of them it yields some pretty confusing code. [link]

    # bad
    Array === something
    (1..100) === 7
    /something/ === some_string
    
    # good
    something.is_a?(Array)
    (1..100).include?(7)
    some_string =~ /something/
    
  • Do not use eql? when using == will do. The stricter comparison semantics provided by eql? are rarely needed in practice. [link]

    # bad:  eql? is the same as == for strings
    "ruby".eql? some_str
    
    # good
    "ruby" == some_str
    1.0.eql? x # eql? makes sense here if want to differentiate between Fixnum and Float 1
    
  • Avoid using Perl-style special variables (like $:, $;, etc.). They are quite cryptic and their use in anything but one-liner scripts is discouraged. Use the human-friendly aliases. Some aliases require the English library. The regular expression capture variables ($1, $2, etc.) are allowed. [link]

    # bad
    $:.unshift File.dirname(__FILE__)
    
    # good:  English not needed
    $LOAD_PATH.unshift File.dirname(__FILE__)
    
    # good
    require "English"
    $FIELD_SEPARATOR = ","
    numbers = "1,2,3".split
    
    # good:  regex capture variables
    "Gray, James" =~ /\A(\w+),\s*(\w+)\z/
    first, last = $2, $1
    
  • Do not put a space between a method name and the opening parenthesis. [link]

    # bad
    f (3 + 2) + 1
    
    # good
    f(3 + 2) + 1
    
  • If the first argument to a method begins with an open parenthesis, always use parentheses in the method invocation. For example, write f((3 + 2) + 1). [link]

  • Always run the Ruby interpreter with the -w option so it will warn you if you forget either of the rules above! [link]

  • Do not use nested method definitions, use lambda instead. Nested method definitions actually produce methods in the same scope (e.g. class) as the outer method. Furthermore, the "nested method" will be redefined every time the method containing its definition is invoked. [link]

    # bad
    def foo(x)
      def bar(y)
        # body omitted
      end
    
      bar(x)
    end
    
    # good:  the same as the previous, but no bar redefinition on every foo call
    def bar(y)
      # body omitted
    end
    
    def foo(x)
      bar(x)
    end
    
    # also good
    def foo(x)
      bar = ->(y) { ... }
      bar.call(x)
    end
    
  • Use the new lambda literal syntax for single line body blocks. Use the lambda method for multi-line blocks. [link]

    # bad
    l = lambda { |a, b| a + b }
    l.call(1, 2)
    
    # passable:  awkward
    l = ->(a, b) {
      tmp = a * 7
      tmp * b / 50
    }
    
    # good
    l = ->(a, b) { a + b }
    l.call(1, 2)
    
    l = lambda { |a, b|
      tmp = a * 7
      tmp * b / 50
    }
    
  • Don't omit the parameter parentheses when defining a stabby lambda with parameters. [link]

    # bad
    l = ->x, y { something(x, y) }
    
    # good
    l = ->(x, y) { something(x, y) }
    
  • Omit the parameter parentheses when defining a stabby lambda with no parameters. [link]

    # bad
    l = ->() { something }
    
    # good
    l = -> { something }
    
  • Prefer proc over Proc.new. [link]

    # bad
    p = Proc.new do |n|
      puts n
    end
    
    # good
    p = proc do |n|
      puts n
    end
    
  • Prefer proc.call() or proc[] over proc.() for both lambdas and procs. [link]

    # bad:  uncommon syntax
    l = ->(v) do
      puts v
    end
    l.(1)
    
    # good
    l = ->(v) do
      puts v
    end
    l.call(1)
    
    # good:  can substitute for places that expect a Hash
    l = ->(v) do
      puts v
    end
    l[1]
    
  • Prefix with _ unused block parameters and local variables. It's also acceptable to use just _ (although it's a bit less descriptive). This convention is recognized by the Ruby interpreter and tools like RuboCop and will suppress their unused variable warnings. [link]

    # bad
    result = hash.map { |k, v| v + 1 }
    
    def something(x)
      unused_var, used_var = something_else(x)
      # ...
    end
    
    # good
    result = hash.map { |_k, v| v + 1 }
    
    def something(x)
      _unused_var, used_var = something_else(x)
      # ...
    end
    
    # good
    result = hash.map { |_, v| v + 1 }
    
    def something(x)
      _, used_var = something_else(x)
      # ...
    end
    
  • Use $stdout/$stderr/$stdin instead of STDOUT/STDERR/STDIN. STDOUT/STDERR/STDIN are constants, and while you can actually reassign (possibly to redirect some stream) constants in Ruby, you'll get an interpreter warning if you do so. [link]

  • Use warn instead of $stderr.puts. Apart from being more concise and clear, warn allows you to suppress warnings if you need to (by setting the warn level to 0 via -W0). [link]

  • Favor the use of sprintf and its alias format over the fairly cryptic String#% method. [link]

    # bad
    "%d %d" % [20, 10]
    # => "20 10"
    
    # good
    sprintf("%d %d", 20, 10)
    # => "20 10"
    
    # good
    sprintf("%{first} %{second}", first: 20, second: 10)
    # => "20 10"
    
    format("%d %d", 20, 10)
    # => "20 10"
    
    # good
    format("%{first} %{second}", first: 20, second: 10)
    # => "20 10"
    
  • Favor the use of Array#join over the fairly cryptic Array#* with a string argument. [link]

    # bad
    %w[one two three] * ", "
    # => "one, two, three"
    
    # good
    %w[one two three].join(", ")
    # => "one, two, three"
    
  • Use Array() instead of an explicit Array check, when dealing with a variable you want to treat as an Array, but you're not certain it is one. [link]

    # bad
    paths = [paths] unless paths.is_a?(Array)
    paths.each do |path|
      do_something(path)
    end
    
    # good
    Array(paths).each do |path|
      do_something(path)
    end
    
  • Use ranges or Comparable#between? instead of complex comparison logic when possible. [link]

    # bad
    do_something if x >= 1000 && x <= 2000
    
    # good
    do_something if (1000..2000).include?(x)
    
    # good
    do_something if x.between?(1000, 2000)
    
  • Favor the use of predicate methods to explicit comparisons with ==. Numeric comparisons are OK. [link]

    # bad
    if x % 2 == 0
    end
    
    if x % 2 == 1
    end
    
    if x == nil
    end
    
    # good
    if x.even?
    end
    
    if x.odd?
    end
    
    if x.nil?
    end
    
    if x.zero?
    end
    
    if x == 0
    end
    
  • Don't do explicit non-nil checks unless you're dealing with boolean values. [link]

      # bad
      do_something if !something.nil?
      do_something if something != nil
    
      # good
      do_something if something
    
      # good:  dealing with a boolean
      def value_set?
        !@some_boolean.nil?
      end
    
  • Avoid the use of BEGIN blocks outside of one-liners. [link]

  • Do not use END blocks outside of one-liners. Use Kernel#at_exit instead. [link]

    # bad
    END { puts "Goodbye!" }
    
    # good
    at_exit do
      puts "Goodbye!"
    end
    
  • Avoid the use of flip-flops outside of one-liners. [link]

  • Avoid use of nested conditionals for flow of control. [link]

    Prefer a guard clause when you can assert invalid data. A guard clause is a conditional statement at the top of a function that bails out as soon as it can.

    # bad
    def compute_thing(thing)
      if thing[:foo]
        update_with_bar(thing)
        if thing[:foo][:bar]
          partial_compute(thing)
        else
          re_compute(thing)
        end
      end
    end
    
    # good
    def compute_thing(thing)
      return unless thing[:foo]
      update_with_bar(thing[:foo])
      return re_compute(thing) unless thing[:foo][:bar]
      partial_compute(thing)
    end
    

    Prefer next in loops instead of conditional blocks.

    # bad
    [0, 1, 2, 3].each do |item|
      if item > 1
        puts item
      end
    end
    
    # good
    [0, 1, 2, 3].each do |item|
      next unless item > 1
      puts item
    end
    
  • Prefer map over collect, find over detect, select over find_all, and inject over reduce. This is not a hard requirement; if the use of the alias enhances readability, it's ok to use it. The rhyming methods are inherited from Smalltalk and are not common in other programming languages. The reason the use of select is encouraged over find_all is that it goes together nicely with reject and its name is pretty self-explanatory. inject has been in Ruby longer and thus is just more widely used than reduce. [link]

  • Don't use count as a substitute for size. For Enumerable objects other than Array it will iterate the entire collection in order to determine its size. [link]

    # bad
    some_hash.count
    
    # good
    some_hash.size
    
  • Use flat_map instead of map + flatten. This does not apply for arrays with a depth greater than 2, i.e. if users.first.songs == ["a", ["b", "c"]], then use map + flatten rather than flat_map. flat_map flattens the array by 1, whereas flatten flattens it all the way. [link]

    # bad
    all_songs = users.map(&:songs).flatten.uniq
    
    # good
    all_songs = users.flat_map(&:songs).uniq
    
  • Prefer reverse_each to reverse.each because some classes that include Enumerable will provide an efficient implementation. Even in the worst case where a class does not provide a specialized implementation, the general implementation inherited from Enumerable will be at least as efficient as using reverse.each. [link]

    # bad
    array.reverse.each do
      ...
    end
    
    # good
    array.reverse_each do
      ...
    end