Beautiful code invites a programmer to learn from it, maintain it, and extend it. Ugly code begs to be thrown out and re-written. Consistent code is more beautiful and easier to read that inconsistent code. Adopting a consistent style will make code more beautiful, and increase its lifespan and impact. This post explains the Ruby style that I have developed from my experience writing Ruby, JavaScript, Java, Python, C++, and Objective C.
My style mostly agrees with Chris Neukirchen’s. Read his if you’re looking for a more condensed summary. Some rules are inspired from Google’s Ruby style guide, which will most likely never be released.
Basic Formatting
80 characters per line. UI studies suggest that people are most efficient at reading text that is 72 characters / line. The limit of 80 accounts for the fact that source code has more punctuation than English text. The limit also allows fitting two code windows side-by-side on a laptop (code / tests), or three windows on a desktop (HTML / CSS / JS).
2-space indentation. Larger indentation might make code easier to read, but definitely makes it harder to fit a decent amount of code on 80-character lines.
No tabs. Editors and browsers may assign different widths to tabs. Decent editors have an option named along the lines of “Use spaces for tabs” that will do the magic for you.
Avoid trailing whitespace, but don’t go out of your way to remove it. Eclipse-based IDEs trash up the code with trailing whitespace. Write your code as cleanly as possible. Don’t submit pull requests consisting solely of removing trailing whitespace, but feel free to piggyback whitespace removal to other stuff.
Blank line between top-level class, module, and/or method definitions. Top-level definitions serve as sections in a source file. Spacing helps visually break down the file into sections.
1 2 3 4 5 6 7 8 9 |
|
Spacing
Prefer 1 space around operators and keywords. This makes the code easy
to read and matches the convention used in all mainstream languages, including
C, JavaScript, and Python. Examples: a + b
, a ? b : c
, while a < b
.
Exceptions to this rule follow below.
No spaces around the .
method dispatcher. Readable and matches other
languages. Example: receiver.message
.
No spaces between unary operators and their operands. Readable and matches
other languages. Examples: !a
, -5
, ~1
.
No spaces after (
and [
. No spaces before ]
and )
. Readable and
matches other languages. Examples: to_s(1)
, array[i]
.
No spaces between a block’s argument list and the |
s wrapping the list.
Examples: map { |i| i * 2 }
, each_with_index do |e, i|
.
1 space between #
and comment text. No spaces make the comment hard to
read. More spaces waste characters.
2 spaces between code and #
comment on the same line. One space makes it
easy to confuse the #
with an operator like +
/ *
. More spaces would waste
characters.
No space before the beginning of the line. Mentioned for completeness, as this should be common sense. Extra spaces would ruin the indentation and make the code hard to read, asides from eating up precious screen estate.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
Comments
#
-comments. =begin
and =end
are only suitable for a top-level
comment providing an overview of the file and/or class. They’re harder to
visually separate from source code than #
-prefixed comment lines.
Each class and method must have a doc comment. Methods without
specifications are unsightly in documentation. For internal methods, or
overrides of well-known methods, such as to_s
, use YARD’s @see
, or RDoc’s
:nodoc:
.
YARD tags with RDoc Markup by default. Use YARD syntax for new projects. Whenever applicable, use RDOC markup on the YARD-annotated code. When modifying old code, either overhaul the entire project, or use the existing conventions for .
Doc comments start with a one-line summary. Short summaries make doc pages pretty and play well with code-completion UIs.
Blank line between summary and the rest of a doc comment. This lets the summary stand out, so readers can skip over the details if they desire.
Comments before the code they apply to. Really small comments can fit on the same line as the code they refer to. All other comments should be placed on their own line(s), before the code they’re referring to, because that’s where programmers and documentation tools expect them to be.
No blank lines between comments and the code they apply to. This way, the comments are visually associated with the code that they apply to. Furthermore, some documentation tools ignore doc-comments if there’s a space between them and their subject class / module / method.
Prefer full sentences in comments. Comments should read like the code’s author thinking out loud in English, and un-terminated thoughts are bad.
Comment a class or module’s end
line with the class / module’s
fully-qualified name. Specify if it’s a class, bona-fide module, or
namespace. This is especially helpful for long / nested modules and classes.
It’s hard to know in advance which parts of your code will grow, so tag all your
end
s. A “bona-fide” module contains instance methods, and is meant to be
included in other classes. A namespace is a module that is solely intended as a
container for inner classes and/or class methods.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
Parentheses
No parentheses around clauses for if
, while
, case
, when
, etc. This
is Ruby, not C or Java. Parentheses make the code more crowded than white-space
would.
No empty parentheses after def
with no arguments. The def
keyword and
source file layout make it obvious that a function is being defined.
Parentheses after def
with arguments. Normally, we’d prefer as few
parentheses as possible. However, this exception is makes it easier to
distinguish between method definitions and calls.
No parentheses in method calls without arguments. Other languages recommend
or need ()
to distinguish between instance variable accesses and method calls.
Ruby has the @
prefix for instance variables.
No parentheses in outermost method calls, when possible. This makes the code
read more like English text. You’ll need parentheses for call chaining, or if
you use {
…}
-blocks, so Ruby can parse your code correctly.
Parentheses in inner calls with arguments. Parentheses are unsightly, but
necessary to avoid ambiguity. For example, foo bar baz
can either mean
foo(bar(baz))
or (foo(bar))(baz)
. Treat keywords like return
, next
and
yield
as method calls.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
Line Breaks
Avoid line breaks. Long expressions are harder to reason about. Extracting parts of a complicated expression into named local variables will usually help you reduce the expression’s length. The local variable names also serve as documentation, and might help you write fewer comments.
80-characters per line. Obey the line limit whenever the language allows you to. The best way to observe the limit is to enable the “show print margin” setting in your text editor. This will draw a line at the 80-character mark, and you will be bothered if your code crosses it.
Don’t break long URLs. Long URLs are the main exception to the line limit. If you must include a long URL, try to have a line break right before it, to minimize the scrolling required to read your code.
No \
at the end of a line. If a line ends in an incomplete expression,
Ruby will recognize that as a sign that the code continues on the next line.
Placing your line breaks right after operators, like +
, or right after ,
(commas), makes it obvious that a line isn’t terminated.
4-space hanging indentation. The 4-space indentation contrasts with the 2-space indentation for regular code lines. This makes it easy to distinguish between stand-alone lines of code, and lines that depend on previous lines.
Expression-aware hanging indentation. You may choose to align your line continuations with the beginning of an open expression, or with the first call of a call chain. Do that when it enhances readability. Don’t do it if it would cause even more line breaks.
No hanging indentation for English text in comments. Follow the convention for typesetting English text when writing English text.
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Naming
snake_case for local variables, arguments, methods, instance / class variables, and hash keys / named arguments. This matches most Ruby conventions and existing code.
CamelCase for class and module names. Do not keep acronyms like HTTP and URL
capitalized. HttpCache
is easier to read and type than HTTPCache
.
SCREAMING_CAPS for constant names. This makes it easy to distinguish constants from classes.
Use other
to name the argument of a binary operator. This reads well and
is consistent with other Ruby code.
Use acc, e
or a, e
to name the arguments to an inject
block. Think
“accumulator, element”. Use acc
whenever possible,
revert to a
if line width / code size concerns warrant it. Bonus points if you
come up with names that are more meaningful in your code’s specific context.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
Blocks
Use {
…}
for single-line blocks. The notation is more compact. Even if
you have to add parentheses around a function call, it’s still shorter than
do;
…; end
.
Prefer do
…end
for multi-line blocks. This matches most Ruby code, and
do
behaves less surprisingly than {
. For example, a.inject b { }
parses
as a.inject(this.b() { })
, whereas a.inject b do
parses as a.inject(b) do
.
Use {
…}
for multi-line blocks with call chaining. Ruby supports call
chaining on do
…end
blocks, but it looks weird.
Prefer lambda
over proc
and Proc.new
. It’s shorter, used in other Ruby
code, and understandable to most Computer Science majors.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
Language Constructs
Avoid expressions starting with !
(not). In many cases, negative
expressions can be replaced with positive expressions, in conjunction with
replacing if
with unless
, while
with until
, and select
with reject
.
No for
. The each
+ block syntax makes for more idiomatic Ruby
than for
…in
, which looks like JavaScript and Python. Some Rails generators
produce code with for
…in
; change the code and, whenever possible, upgrade
the generators.
No then
. The then
keyword is not necessary if the if
expression is
specified on the next line after the clause. For one-line if
expressions, use
the ?:
ternary operator.
No return
on the last line of a method. Ruby methods automatically return
the result of the last expression in a method.
Avoid self.
method calls. Method calls without a receiver are
automatically performed on self
. Use self.
if Ruby would otherwise confuse
a writer method call and a local variable, for example in self.name = "Value"
.
Use a ||= b
for default values. It’s a Ruby idiom for assigning b
to a
if a
is nil
. It’s also shorter than var = default unless var
or other
equivalents.
Use a && a.b
for nil
handling. Also a Ruby idom, useful for calling b
on a
only if a
is not nil
. It is more compact than an if
…else
expression.
Prefer alias_method
over alias
. alias_method
requires symbols, which
makes it obvious that no method call is being performed.
Built-in Methods
Prefer named methods to indexing and slicing. array.first
reads better
than array[0]
, and array.last
reads better than array[-1]
. However, don’t
be afraid to use a clever slice to avoid a method chain.
Prefer map
over collect
, find
over detect
, length
over size
.
map
is inherited from LISP, exists in JavaScript and Python, and is known to
most Computer Science majors. length
exists in JavaScript, and is similar to
len
in Python and to strlen
in C.
Source Encoding
UNIX newlines. You don’t need to worry about this unless you’re on Windows.
If you’re on Windows, configure your editor and/or git client to use UNIX style
(also known as LF
or \n
) line endings.
7-bit ASCII for code. Stick to the 7-bit ASCII character set for everything, unless you need to use non-English language in UI strings.
No magic encoding comment for ASCII files. If your source code only uses
ASCII, don’t add a
magic encoding comment
to your files. Remember that Ruby’s default encoding is most likely UTF-8, so
don’t assume that your strings will be encoded using ASCII-8BIT
.
1
|
|
Use UTF-8 if 7-bit ASCII doesn’t work. If you must use characters outside 7-bit ASCII in UI strings, use UTF-8. Most software that your code might need to interface with (e.g. browsers, database servers) uses UTF-8 by default.
Magic encoding comment for UTF-8 files. If your source code has non-ASCII characters, always add a magic encoding comment to it. Remember that the magic comment must be on the first line of the file. If the first line is a shebang line, the magic comment must immediately follow it. Always leave a blank line after the magic comment.
1 2 3 |
|
1 2 3 4 |
|
In very rare cases (stubbing low-level functions such as Socket.recv
), it
might be convenient to set the source encoding to ASCII-8BIT
(a.k.a.
BINARY
). Only do this in test code. Prefer calling force_encoding
on
Strings, and only use an encoding magic if the test case would be littered with
force_encoding
calls.