We use Bozhidar Batsov's Ruby coding style guide for our team. I strongly recommend to read it from start to finish. Current style guide is almost just important parts of it. Why "almost"? There are several differences from the bbatsov guide - I will mark that rules by UNLIKE BBATSOV GUIDE text.
- Source Code Layout
- Syntax
- Naming
- Comments
- Classes
- Exceptions
- Collections
- Strings
- Regular Expressions
- Percent Literals
- Metaprogramming
- Misc
-
Use
UTF-8
as the source file encoding. -
Use two spaces per indentation level (aka soft tabs). No hard tabs.
-
Use Unix-style line endings. (*BSD/Solaris/Linux/OS X users are covered by default, Windows users have to be extra careful.)
-
If you're using Git you might want to add the following configuration setting to protect your project from Windows line endings creeping in:
$ git config --global core.autocrlf true
-
-
Don't use
;
to separate statements and expressions. As a corollary - use one expression per line.# bad puts 'foobar'; # superfluous semicolon puts 'foo'; puts 'bar' # two expressions on the same line # good puts 'foobar' puts 'foo' puts 'bar' puts 'foo', 'bar' # this applies to puts in particular
-
Use spaces around operators, after commas, colons and semicolons, around
{
and before}
. Whitespace might be (mostly) irrelevant to the Ruby interpreter, but its proper use is the key to writing easily readable code.sum = 1 + 2 a, b = 1, 2 [1, 2, 3].each { |e| puts e } class FooError < StandardError; end
The only exception, regarding operators, is the exponent operator:
# bad e = M * c ** 2 # good e = M * c**2
-
No spaces after
(
,[
or before]
,)
. -
No space after
!
. -
Indent
when
as deep ascase
.# bad case when song.name == 'Misty' puts 'Not again!' when song.duration > 120 puts 'Too long!' when Time.now.hour > 21 puts "It's too late" else song.play end # good case when song.name == 'Misty' puts 'Not again!' when song.duration > 120 puts 'Too long!' when Time.now.hour > 21 puts "It's too late" else song.play end
-
Use empty lines between method definitions and also to break up a method into logical paragraphs internally.
def some_method data = initialize(options) data.manipulate! data.result end def some_method result end
-
Use spaces around the
=
operator when assigning default values to method parameters:# bad def some_method(arg1=:default, arg2=nil, arg3=[]) # do something... end # good def some_method(arg1 = :default, arg2 = nil, arg3 = []) # do something... end
-
Align the parameters of a method call if they span more than one line. When aligning parameters is not appropriate due to line-length constraints, single indent for the lines after the first is also acceptable.
# starting point (line is too long) def send_mail(source) Mailer.deliver(to: '[email protected]', from: '[email protected]', subject: 'Important message', body: source.text) end # bad (double indent) def send_mail(source) Mailer.deliver( to: '[email protected]', from: '[email protected]', subject: 'Important message', body: source.text) end # good def send_mail(source) Mailer.deliver(to: '[email protected]', from: '[email protected]', subject: 'Important message', body: source.text) end # good (normal indent) def send_mail(source) Mailer.deliver( to: '[email protected]', from: '[email protected]', subject: 'Important message', body: source.text ) end
-
Limit lines to 80 characters.
-
Avoid trailing whitespace.
-
End each file with a newline.
-
Don't use block comments.
-
Use
def
with parentheses when there are arguments. Omit the parentheses when the method doesn't accept any arguments.# bad def some_method() # body omitted end # good def some_method # body omitted end # bad def some_method_with_arguments arg1, arg2 # body omitted end # good def some_method_with_arguments(arg1, arg2) # body omitted end
-
Never use
for
, unless you know exactly why. Most of the time iterators should be used instead.for
is implemented in terms ofeach
(so you're adding a level of indirection), but with a twist -for
doesn't introduce a new scope (unlikeeach
) and variables defined in its block will be visible outside it.# bad for elem in arr do puts elem end # good arr.each { |elem| puts elem }
-
Favor the ternary operator(
?:
) overif/then/else/end
constructs. It's more common and obviously more concise.# 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.# 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
-
Leverage the fact that
if
andcase
are expressions which return a result.# 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 syntaxwhen x: ...
has been removed as of Ruby 1.9. -
Use
!
instead ofnot
. -
Avoid the use of
!!
. -
The
and
andor
keywords are banned. It's just not worth it. Always use&&
and||
instead. -
Avoid multi-line
?:
(the ternary operator); useif/unless
instead. -
Favor modifier
if/unless
usage when you have a single-line body. Another good alternative is the usage of control flow&&/||
.# bad if some_condition do_something end # good do_something if some_condition # another good option some_condition && do_something
-
Favor
unless
overif
for negative conditions (or control flow||
). -
Never use
unless
withelse
. Rewrite these with the positive case first.# bad unless success? puts 'failure' else puts 'success' end # good if success? puts 'success' else puts 'failure' end
-
Feel free to use parentheses around the condition of an
if/unless/while/until
. UNLIKE BBATSOV GUIDEif (x > 10) # body omitted end if x > 10 # body omitted end
-
Favor
until
overwhile
for negative conditions.# bad do_something while !some_condition # good do_something until some_condition
-
Use
Kernel#loop
instead ofwhile/until
when you need an infinite loop.# bad while true do_something end # good loop do do_something end
-
Omit the outer braces around an implicit options hash.
# 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.
class Person < ActiveRecord::Base # bad validates(:name, { presence: true, length: { within: 1..10 } }) # good validates :name, presence: true, length: { within: 1..10 } end
-
Prefer
{...}
overdo...end
for single-line blocks. Avoid using{...}
for multi-line blocks (multiline chaining is always ugly). Always usedo...end
for "control flow" and "method definitions" (e.g. in Rakefiles and certain DSLs). Avoiddo...end
when chaining.names = ['Bozhidar', 'Steve', 'Sarah'] # bad names.each do |name| puts name end # good names.each { |name| puts name } # bad names.select do |name| name.start_with?('S') end.map { |name| name.upcase } # good names.select { |name| name.start_with?('S') }.map { |name| name.upcase }
-
Avoid
return
where not required for flow of control.# bad def some_method(some_arr) return some_arr.size end # good def some_method(some_arr) some_arr.size end
-
Using the return value of
=
(an assignment) is ok. UNLIKE BBATSOV GUIDE# good if v = array.grep(/foo/) ... # also good - has correct precedence. if (v = next_value) == "hello" ...
-
Use shorthand self assignment operators whenever applicable.
# 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
||=
freely to initialize variables (UNLIKE BBATSOV GUIDE), but don't use ||= to initialize boolean variables. (Consider what would happen if the current value happened to be false.)# set name to Bozhidar, only if it's nil or false, name ||= "Bozhidar" # bad - would set enabled to true even if it was false enabled ||= true # good enabled = true if enabled.nil?
-
Avoid using Perl-style special variables (like
$0-9
,$
, etc. ). They are quite cryptic and their use in anything but one-liner scripts is discouraged. Prefer long form versions such as$PROGRAM_NAME
. UNLIKE BBATSOV GUIDE -
Never put a space between a method name and the opening parenthesis.
# 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)
. -
Use the new lambda literal syntax for single line body blocks. Use the
lambda
method for multi-line blocks.# bad l = lambda { |a, b| a + b } l.call(1, 2) # correct, but looks extremely awkward l = ->(a, b) do tmp = a * 7 tmp * b / 50 end # good l = ->(a, b) { a + b } l.call(1, 2) l = lambda do |a, b| tmp = a * 7 tmp * b / 50 end
-
Prefer
proc
overProc.new
.# bad p = Proc.new { |n| puts n } # good p = proc { |n| puts n }
-
Prefer
proc.call()
overproc[]
orproc.()
for both lambdas and procs.# bad - looks similar to Enumeration access l = ->(v) { puts v } l[1] # also bad - uncommon syntax l = ->(v) { puts v } l.(1) # good l = ->(v) { puts v } l.call(1)
-
You can use
_
for unused block parameters.result = hash.map { |_, v| v + 1 }
-
Don't do explicit non-
nil
checks unless you're dealing with boolean values.# 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 use of nested conditionals for flow of control.
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
-
Use
snake_case
for symbols, methods and variables. -
Use
CamelCase
for classes and modules. (Keep acronyms like HTTP, RFC, XML uppercase.) -
Use
snake_case
for naming files, e.g.hello_world.rb
. -
Use
snake_case
for naming directories, e.g.lib/hello_world/hello_world.rb
. -
Aim to have just a single class/module per source file. Name the file name as the class/module, but replacing CamelCase with snake_case.
-
Use
SCREAMING_SNAKE_CASE
for other constants. -
The names of predicate methods (methods that return a boolean value) should end in a question mark. (i.e.
Array#empty?
). Methods that don't return a boolean, shouldn't end in a question mark. -
The names of potentially "dangerous" methods (i.e. methods that modify
self
or the arguments,exit!
, etc.) should end with an exclamation mark. Bang methods should only exist if a non-bang method exists. (More on this).
-
Write self-documenting code and ignore the rest of this section. Seriously!
-
Don't use block comments.
-
Write comments in English.
-
Use
TODO
to note missing features or functionality that should be added at a later date.
-
Use a consistent structure in your class definitions.
class Person # extend and include go first extend SomeModule include AnotherModule # inner classes CustomErrorKlass = Class.new(StandardError) # constants are next SOME_CONSTANT = 20 # afterwards we have attribute macros attr_reader :name # followed by other macros (if any) validates :name # public class methods are next in line def self.some_method end # followed by public instance methods def some_method end # protected and private methods are grouped near the end protected def some_protected_method end private def some_private_method end end
-
Prefer modules to classes with only class methods. Classes should be used only when it makes sense to create instances out of them.
-
Favor the use of
module_function
overextend self
when you want to turn a module's instance methods into class methods.# bad module Utilities extend self def parse_something(string) # do stuff here end def other_utility_method(number, string) # do some more stuff end end # good module Utilities module_function def parse_something(string) # do stuff here end def other_utility_method(number, string) # do some more stuff end end
-
Try to make your classes as SOLID as possible.
-
Consider using
Struct.new
, which defines the trivial accessors, constructor and comparison operators for you.# good class Person attr_accessor :first_name, :last_name def initialize(first_name, last_name) @first_name = first_name @last_name = last_name end end # better Person = Struct.new(:first_name, :last_name) do end
-
Use
def self.method
to define singleton methods. This makes the code easier to refactor since the class name is not repeated.class TestClass # bad def TestClass.some_method # body omitted end # good def self.some_other_method # body omitted end # Also possible and convenient when you # have to define many singleton methods. class << self def first_method # body omitted end def second_method_etc # body omitted end end end
-
Don't use exceptions for flow of control.
# bad begin n / d rescue ZeroDivisionError puts 'Cannot divide by 0!' end # good if d.zero? puts 'Cannot divide by 0!' else n / d end
-
Avoid rescuing the
Exception
class. This will trap signals and calls toexit
, requiring you tokill -9
the process.# bad begin # calls to exit and kill signals will be caught (except kill -9) exit rescue Exception puts "you didn't really want to exit, right?" # exception handling end # good begin # a blind rescue rescues from StandardError, not Exception as many # programmers assume. rescue => e # exception handling end # also good begin # an exception occurs here rescue StandardError => e # exception handling end
-
Prefer literal array and hash creation notation (unless you need to pass parameters to their constructors, that is).
# bad arr = Array.new hash = Hash.new # good arr = [] hash = {}
-
When accessing the first or last element from an array, prefer
first
orlast
over[0]
or[-1]
. -
Use
Set
instead ofArray
when dealing with unique elements.Set
implements a collection of unordered values with no duplicates. This is a hybrid ofArray
's intuitive inter-operation facilities andHash
's fast lookup. -
Prefer symbols instead of strings as hash keys.
# bad hash = { 'one' => 1, 'two' => 2, 'three' => 3 } # good hash = { one: 1, two: 2, three: 3 }
-
Use the Ruby 1.9 hash literal syntax when your hash keys are symbols.
# bad hash = { :one => 1, :two => 2, :three => 3 } # good hash = { one: 1, two: 2, three: 3 }
-
Don't mix the Ruby 1.9 hash syntax with hash rockets in the same hash literal. When you've got keys that are not symbols stick to the hash rockets syntax.
-
Never modify a collection while traversing it.
-
Prefer string interpolation and string formatting instead of string concatenation:
# bad email_with_name = user.name + ' <' + user.email + '>' # good email_with_name = "#{user.name} <#{user.email}>" # good email_with_name = format('%s <%s>', user.name, user.email)
-
Avoid using
String#+
when you need to construct large data chunks. Instead, useString#<<
. Concatenation mutates the string instance in-place and is always faster thanString#+
, which creates a bunch of new string objects.# good and also fast html = '' html << '<h1>Page title</h1>' paragraphs.each do |paragraph| html << "<p>#{paragraph}</p>" end
-
Don't use regular expressions if you just need plain text search in string:
string['text']
-
Avoid using numbered groups as it can be hard to track what they contain. Named groups can be used instead.
# bad /(regexp)/ =~ string ... process Regexp.last_match[1] # good /(?<meaningful_var>regexp)/ =~ string ... process meaningful_var
-
Be careful with
^
and$
as they match start/end of line, not string endings. If you want to match the whole string use:\A
and\z
(not to be confused with\Z
which is the equivalent of/\n?\z/
). -
Use
x
modifier for complex regexps. This makes them more readable and you can add some useful comments. Just be careful as spaces are ignored.regexp = / start # some text \s # white space char (group) # first group (?:alt1|alt2) # some alternation end /x
-
Use
%w
freely.STATES = %w(draft open closed)
-
Use
%()
(it's a shorthand for%Q
) for single-line strings which require both interpolation and embedded double-quotes. For multi-line strings, prefer heredocs.# bad (no interpolation needed) %(<div class="text">Some text</div>) # should be '<div class="text">Some text</div>' # bad (no double-quotes) %(This is #{quality} style) # should be "This is #{quality} style" # bad (multiple lines) %(<div>\n<span class="big">#{exclamation}</span>\n</div>) # should be a heredoc. # good (requires interpolation, has quotes, single line) %(<tr><td class="name">#{name}</td>)
-
Use
%r
only for long regular expressions matching more than one '/' character.# bad %r(\s+) # still bad %r(^/(.*)$) # should be /^\/(.*)$/ # good %r(^/blog/2011/(.*)$)
-
Prefer
()
as delimiters for all%
literals, except%r
. Since braces often appear inside regular expressions in many scenarios a less common character like{
might be a better choice for a delimiter, depending on the regexp's content.# bad %w[one two three] %q{"Test's king!", John said.} # good %w(one two three) %q("Test's king!", John said.)
-
Avoid needless metaprogramming.
-
Do not mess around in core classes when writing libraries. (Do not monkey-patch them.)
-
Avoid parameter lists longer than three or four parameters.
-
If you really need "global" methods, add them to Kernel and make them private.
-
Use module instance variables instead of global variables.
# bad $foo_bar = 1 # good module Foo class << self attr_accessor :bar end end Foo.bar = 1
-
Prefer
Time.now
overTime.new
when retrieving the current system time. -
Code in a functional way, avoiding mutation when that makes sense.
-
Use common sense.