I. Avoid Allocating a Lot of Objects

Example:

DON’T

1_000_000.times { "some string or object" }

SHOULD BE

my_invariant = "some string or object"
1_000_000.times { my_invariant }

OR

1_000_000.times { "some string or object".freeze }

II. Do Not Use Exceptions for Control Flow

DON’T

def with_rescue
  self.mythical_method
rescue NoMethodError
  nil
end

SHOULD BE

def with_condition
  respond_to?(:mythical_method) ? self.mythical_method : nil
end

III. Be Careful with Calculations within Iterators

DON’T

def func(array)
  array.inject({}) { |h, e| h.merge(e => e) }
end

SHOULD BE

def func(array)
  array.inject({}) { |h, e| h[e] = e; h }
end

IV. Avoid Object Creation if Possible

1. String Concatenation

Avoid using += to concatenate strings in favor of the << method

str1 = "first"
str2 = "second"
str1.object_id       # => 16241320

DON’T

str1 += str2    # str1 = str1 + str2
str1.object_id  # => 16241240, id is changed

SHOULD BE

str1 << str2
str1.object_id  # => 16241240, id is the same

When you use +=, Ruby creates a temporary object which is the result of str1 + str2. Then it overrides the str1 variable with a reference to the newly built object. On the other hand, << modifies the existing one.

As a result of using +=, you have the following disadvantages:

  • More calculations to join strings.
  • A redundant string object in memory (previous value of str1), which approximates the time when GC will trigger.

2. Use Bang! Methods

In many cases, bang methods do the same as their non-bang analogs but without duplicating an object.

array = ['a', 'b', 'c']
array.map(&:upcase)
array.map!(&:upcase)

Some methods are essentially an alias for bang methods

2.1 select vs. keep_if

a = %w{ a b c d e f }
a.keep_if { |v| v =~ /[aeiou]/ }
puts a
=> ["a", "e"]
a = %w{ a b c d e f }
a.select { |v| v =~ /[aeiou]/ }
puts a
=> ["a", "b", "c", "d", "e", "f"]

2.2 reject vs. delete_if

a = [1,2,3,4,5]
a.delete_if(&:even?)
puts a
=> [1,3,5]

3. Parallel Assignment is Slower

DON’T

a, b = 10, 20

SHOULD BE

a = 10
b = 20

V. Use Methods That Are Optimized by Ruby

DON’TSHOULD BE
Enumerable#reverse.eachEnumerable#reverse_each
Enumerable#select.firstEnumerable#detect

READ MORE

VI. Be Careful When Using eval and send

When you use them, please make sure you can handle what will be entered.

DON’T

type = params[:type] # type = "system('rm -rf ./')"
eval(type)
type = params[:type] # type = delete_all
User.send(type)

SHOULD BE

type = params[:type]
User.send(type) if ['abc', 'zyz'].include?(type)