Clean Code (ROR) Md. Tauhidul Islam Software Engineer Nascenia Ltd.
Clean Code (ROR)
Md. Tauhidul IslamSoftware Engineer
Nascenia Ltd.
Table of Content
Source Code Layout
Syntax
Comments
Source Code Layout
Indentation
Use two spaces per indentation level (aka soft tabs). No hard tabs.
# bad - four spacesdef some_method do_somethingend
# good - two spacedef some_method do_somethingend
Line endings
Use Unix-style line endings. (*BSD/Solaris/Linux/OS X users are covered by default, Windows users have to be extra careful.)
$ git config --global core.autocrlf true
Statements & Expressions
Don't use ; to separate statements and expressions. As a corollary - use one expression per line.
# goodputs 'foobar'puts 'foo'puts 'bar'puts 'foo', 'bar'
# badputs 'foobar';puts 'foo'; puts 'bar'
Use spaces
Use spaces around operators, after commas, colons and semicolons.
The only exception is the exponent operator
sum = 1 + 2a, b = 1, 2class FooError < StandardError; end
# bade = M * c ** 2
# goode = M * c**2
Use spaces
No spaces after (, [ or before ], ). Use spaces around { and before }
For hash literals two styles are considered acceptable
With interpolated expressions, there should be no padded-spacing inside the braces.
{one: 1, two: 2}{ one: 1, two: 2 }
[1, 2, 3].each { |e| puts e }
# bad"From: #{ user.name }"
# good"From: #{user.name}"
Use spaces
No space after !
No space inside range literals
# bad! something
# good!something
# bad1 .. 3'a' … 'z'
# good1..3'a'...'z'
When-Case
Indent when as deep as case
# badcase 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.playend
# goodcasewhen 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.playend
Alignment: assigning a conditional expression
When assigning the result of a conditional expression to a variable, preserve the usual alignment of its branches
kind = case year when 1850..1889 then 'Blues' when 1890..1909 then 'Ragtime' when 1910..1929 then 'New Orleans Jazz' when 1930..1939 then 'Swing' when 1940..1950 then 'Bebop' else 'Jazz' end
result = if some_cond calc_something else calc_something_else end
Empty lines
Use empty lines between method definitions and also to break up methods into logical paragraphs internally
def some_method data = initialize(options)
data.manipulate!
data.resultend
def some_method resultend
Assigning default values to method parameters
Use spaces around the = operator when assigning default values to method parameters
# baddef some_method(arg1=:default, arg2=nil, arg3=[]) # do something...End
# gooddef some_method(arg1 = :default, arg2 = nil, arg3 = []) # do something...end
Align the parameters of a method call
When aligning parameters is not appropriate due to line-length constraints, single indent for the lines after the first is also acceptable
def send_mail(source) Mailer.deliver( to: '[email protected]', from: '[email protected]', subject: 'Important message', body: source.text )end
def send_mail(source) Mailer.deliver(to: '[email protected]', from: '[email protected]', subject: 'Important message', body: source.text)end
Align the elements of array
Align the elements of array literals spanning multiple lines
# bad - single indentmenu_item = ['Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Baked beans', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam']
# goodmenu_item = [ 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Baked beans', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam']
# goodmenu_item = ['Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Baked beans', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam']
Add underscores to large numeric
Add underscores to large numeric literals to improve their readability
# bad - how many 0s are there?num = 1000000
# good - much easier to parse for the human brainnum = 1_000_000
Use Rdoc and its conventions for API documentation. Don't put an empty line between the comment block and the def
Limit lines to 80 characters
Avoid trailing whitespace
End each file with a newline
Syntax
Methods with parentheses
Use def with parentheses when there are parameters
# baddef some_method() # body omittedend
# gooddef some_method # body omittedend
# baddef some_method_with_parameters param1, param2 # body omittedend
# gooddef some_method_with_parameters(param1, param2) # body omittedend
Optional arguments
Define optional arguments at the end of the list of arguments
# baddef some_method(a = 1, b = 2, c, d) puts "#{a}, #{b}, #{c}, #{d}"end
# gooddef some_method(c, d, a = 1, b = 2) puts "#{a}, #{b}, #{c}, #{d}"end
Parallel assignment
Avoid the use of parallel assignment for defining variables
# bada, b, c, d = 'foo', 'bar', 'baz', 'foobar'
# gooda = 'foo'b = 'bar'c = 'baz'd = 'foobar'
a = 'foo'b = 'bar'
a, b = b, a
puts a # => 'bar'puts b # => 'foo'
Nested ternary operator
Use one expression per branch in a ternary operator. This also means that ternary operators must not be nested
# badsome_condition ? (nested_condition ? nested_something : nested_something_else) : something_else
# goodif some_condition nested_condition ? nested_something : nested_something_elseelse something_elseend
Expressions which return a result
Leverage the fact that if and case are expressions which return a result
# goodresult = if condition x else y end
# badif condition result = xelse result = yend
result = condition ? x : y
and - or
Always use && and || instead of “and” and “or”
# badif some_condition and some_other_condition do_somethingend
# goodif some_condition && some_other_condition do_somethingend
Condition of single-line body
Favor modifier if/unless usage when you have a single-line body
# badif some_condition do_somethingend
# gooddo_something if some_condition
# another good optionsome_condition && do_something
Avoid if for negative conditions
Favor unless over if for negative conditions (or control flow ||)
# baddo_something if !some_condition
# baddo_something if not some_condition
# gooddo_something unless some_condition
# another good optionsome_condition || do_something
unless with else
Do not use unless with else. Rewrite these with the positive case first
# goodif success? puts 'success'else puts 'failure'end
# badunless success? puts 'failure'else puts 'success'end
Parentheses around the condition
Don't use parentheses around the condition of an if/unless/while/until
# goodif x > 10 # body omittedend
# badif (x > 10) # body omittedend
while/until: single-line body
Favor modifier while/until usage when you have a single-line body
# badwhile some_condition do_somethingend
# gooddo_something while some_condition
while for negative conditions
Favor until over while for negative conditions
# baddo_something while !some_condition
# gooddo_something until some_condition
Parentheses with no arguments
Omit parentheses for method calls with no arguments
# badKernel.exit!()2.even?()fork()'test'.upcase()
# goodKernel.exit!2.even?fork'test'.upcase
Use the proc invocation shorthand when the invoked method is the only operation of a block
# badnames.map { |name| name.upcase }
# goodnames.map(&:upcase)
do...end for blockPrefer {...} over do...end for single-line blocks. Avoid using {...} for multi-line blocks
# badnames.select do |name| name.start_with?('S')end.map { |name| name.upcase }
# goodnames.select { |name| name.start_with?('S') }.map(&:upcase)
# badnames.each do |name| puts nameend
# goodnames.each { |name| puts name }
Return keyword
Avoid return where not required for flow of control
# baddef some_method(some_arr) return some_arr.sizeend
# gooddef some_method(some_arr) some_arr.sizeend
Self keyword
Avoid self where not required. (It is only required when calling a self write accessor.)
# gooddef ready? if last_reviewed_at > last_updated_at worker.update(content, options) self.status = :in_progress end status == :verifiedend
# baddef ready? if self.last_reviewed_at > self.last_updated_at self.worker.update(self.content, self.options) self.status = :in_progress end self.status == :verifiedend
Self assignment operators
Use shorthand self assignment operators whenever applicable
# goodx += yx *= yx **= yx /= yx ||= yx &&= y
# badx = x + yx = x * yx = x**yx = x / yx = x || yx = x && y
Initialize variables if nil
Use ||= to initialize variables only if they're not already initialized
# badname = name ? name : 'Bozhidar'
# badname = 'Bozhidar' unless name
# good - set name to 'Bozhidar', only if it's nil or falsename ||= 'Bozhidar'
Space before parenthesis of method params
Do not put a space between a method name and the opening parenthesis
# badf (3 + 2) + 1
# goodf(3 + 2) + 1
Comments
Comments
Good code is its own best documentation. As you're about to add a comment, ask yourself, "How can I improve the code so that this comment isn't needed?" Improve the code and then document it to make it even clearer.
-- Steve McConnell
Comments
Write self-documenting code and ignore the rest of this section. Seriously!
Write comments in English
Use one space between the leading # character of the comment and the text of the comment
Keep existing comments up-to-date. An outdated comment is worse than no comment at all
Avoid writing comments to explain bad code. Refactor the code to make it self-explanatory
Superfluous comments
Avoid superfluous comments
# badcounter += 1 # Increments counter by one.
Block comments
Don't use block comments
# bad=begincomment lineanother comment line=end
# good# comment line# another comment line
Questions?