8 tricks and best practices for improving your Ruby code
Tags: ruby, webdev, tricks, rails
Published: 2021-03-11 19:00:21

Software developers sometimes think that productivity means writing code as fast as possible. But true productivity with a programming language comes down to the **quality of your code.**
 
Ruby is a unique, versatile language that can be used to build just about anything. Ruby, though simple and easy to learn, requires in-depth knowledge and experience to be used to its full potential. 
So, today, we’ve compiled our best tips, tricks, and best practices to help you make the most of Ruby. 

**This guide at a glance:**
* [Tip 1: Avoid hidden structures](#1)
* [Tip 2: Avoid `while !(not)`](#2)
* [Tip 3: Use `loop do` over  `while(true)`](#3)
* [Tip 4: Use  Double Pipe Equals `||=` for your methods](#4)
* [Tip 5: Patch the Garbage Collector (GC)](#5)
* [Tip 6: Use `Hash[...]` to create a hash from a list of values](#6)
* [Tip 7: Avoid functions that don’t work well with iterators](#7)
* [Tip 8: Follow Ruby best practices](#8)
* [Next steps for your learning](#next)

<br>

<a name="1"></a>

## Tip 1: Avoid hidden structures

Hidden structures will make your Ruby code hard to maintain in the long run, and it is best to avoid them. Take a look at this code:

```ruby
class ObscuringReferences
  def initialize(data)
    @data = data
  end

  def calculate
    data.collect do |cell|
      cell[0] + (cell[1] * 2)
    end
  end

  private

  attr_reader :data
end

ObscuringReferences.new([[100, 25], [250, 22], [984, 30], [610, 42]])

```

What’s the problem here? We are using the array’s index positions to get information for the `calculate` method. However, it’s hard to tell what information we are gaining, because `0` and `1` are **not clearly defined** and therefore not too useful. 

Instead, we can use constants to assign meaningful information to our values, like this:

```ruby
class RevealingReferences
  HEIGTH  = 0
  WIDTH = 1

  def initialize(data)
    @data = data
  end

  def calculate
    data.collect do |value|
      value[HEIGTH] + (value[WIDTH] * 2)
    end
  end

  private

  attr_reader :data
end
```

> We can also avoid hidden Ruby structures by using `Struct` to represent the data in a meaningful way.

<br>

<a name="2"></a>

## Tip 2: Avoid `while !(not)`
The `while !(not)` loop can be used to test code until a condition is not met. In the example below, the condition is essentially saying *“while download is not finished (execute the code block)”*.

```ruby
while !download.is_finished?
  spinner.keep_spinning
end
```

Clearly, this can lead to some confusion because it isn’t natural to the way we think about negatives and positives. Instead, we can use `until`, which is essentially like the the negative version of `while`.

```ruby
until download.is_finished?
  spinner.keep_spinning
end
```

In this revised version, the `until` condition reads more naturally: *“until download is finished (execute the code block)”*. This will help make your code more readable and easier to skim.



<br>
 
<a name="3"></a>

## Tip 3: Use `loop do` over  `while(true)`

The `loop do` offers much cleaner syntax than the `while(true)` conditional for many cases. Take a look at the comparison here:

```ruby
# while 
def play
    get_guess
    while true
        ...
        ...
    end
end
```

```ruby
# loop do
def play
    get_guess
    loop do
        ...
        ...
    end
end
```

In general, `loop do` offers cleaner, better syntax when you're handling with external iterators. Similarly, `loop do` also allows you to loop through two collections simultaneously, which leads to cleaner, easier Ruby code. 

```ruby
iterator = (1..9).each
iterator_two = (1..5).each

loop do
    puts iterator.next
    puts iterator_two.next
end

#=> 1,1,2,2,3,3,4,4,5,5,6.
```

<br>

<a name="4"></a>

## Tip 4: Use Double Pipe Equals `||=` for your methods
 
Double Pipe Equals is a great way to write concise Ruby code. It is equivalent to the following:

```
a || a = b 
```

`a ||= b` works like a conditional assignment operator, so if `a` is undefined or false, then it will evaluate `b` and set `a` to that result. Or, if `a` is defined and true, then `b` will not be evaluated.

This operator is great for creating methods in your classes, especially for **calculations**. 

```ruby 
def total
  @total ||= (1..100000000).to_a.inject(:+)
end
```
<br>

<a name="5"></a>

## Tip 5: Patch the Garbage Collector (GC)

Ruby’s Garbage Collector (GC) is known for being **slow**, especially in versions before 2.1. The algorithm for Ruby’s GC is “mark and-sweep” (the slowest for garbage collectors), and it has to stop the application during garbage collection processes. 

To fix this, we can patch it to include **customizable GC extensions**. This will really help with speed when your Ruby application scales. 

You can use tuning variables based on which version of Ruby you use, and many patches are available on [GitHub](https://www.educative.io/blog/git-github-tutorial-beginners) if you don’t want to hard-code them in. Here are three as an example:
* `RUBY_GC_HEAP_INIT_SLOTS`: initial allocation slots
* `RUBY_GC_HEAP_FREE_SLOTS`: prepare at least this number of slots after GC, and allocate slots if there aren’t enough.
* `RUBY_GC_HEAP_GROWTH_MAX_SLOTS`: limit allocation rate to this number of slots. 

<br>

<a name="6"></a>
## Tip 6: Use `Hash[...]` to create a hash from a list of values

You can easily create a hash from a list in Ruby using `Hash[...]`, for example:

```ruby
Hash['key1', 'value1', 'key2', 'value2']

# => {"key1"=>"value1", "key2"=>"value2"}
```
Imagine that we’ve collected data in a **tabular format**. It is organized with one array representing the column headers and an array of arrays representing the values of our rows.

```ruby
columns = [ "Name", "city", "employed"]
 
rows = [ 
  [ "Matt",   "Seattle",    true ], 
  [ "Amy",  "Chicago", true], 
  [ "Nice", "Lahore",     false ]
]
```

The keys for our final hashes will be the column array that holds our column names, and the tabular data is our array of arrays, holding the rows data. We want to create an array of hashes so that each hash key is the column name, and the row is the value.

```ruby
# Hard way 
results = []

for i in (0...rows.length)  
  h = {}
  for j in (0...columns.length)    
    h[columns[j]] = rows[i][j]    
  end
  results << h
end

results 
```

```ruby
# Easier way
correlated = rows.map{|r| Hash[ columns.zip(r) ] }
```

<br>

<a name="7"></a>

## Tip 7: Avoid functions that don’t work well with iterators

When working with iterators in Ruby, complexity is important. So, we need to avoid slower methods and find an alternative that offers better performance but leads to the same result.

Notably, the method `Date#parse` method is known for **poor performance**. Instead, we should specify an expected date format when parsing our code. For example, say we want to work with the `dd/mm/yyyy` format:

```ruby
Date.strptime(date, '%d/%m/%Y')
```

In terms of type checks, `Object#class`, `Object#kind_of?`, and `Object#is_a?` can lead to poor performance as well when used with loops. Instead, it’s better to write the function calls away from iterators that are frequently called.

<br>

<a name="8"></a>
 
## Tip 8: Follow Ruby best practices

Best practices with Ruby help us to write code that is concise and easy to work with down the line. Let’s look at a few that you can implement today. 
* When possible, use existing gems instead of writing code from scratch for more optimized code.
* To avoid using for loops, use the `each` method for a block
* Use ternary operators to write shorter code 
* Instead of writing length `if` statements, use `case/when` and `unless/until/while` conditionals
* Use the splat `*` operator to group inputs into an array when your methods have an unfixed number of inputs.
use symbols instead of strings in hashes
* Avoid using comments unless they are necessary. Ideally, your code should be clear enough on its own to not need commenting. 
* Use the double bang `!!` to write methods that determine if a given value exists
* Use an APM during development. This will help you immediately know when something new to your code has caused a drop in performance. 
* When writing methods yourself, remember two simple rules: a method should only do one thing, and it needs a clear name that reflects this singular purpose.


<br>


## Next steps for your learning 
These tips and best practices will help you write more readable and clearer Ruby code for improved performance. Ruby is a great language, with a lot of quirky syntactical tricks. To make the most of this language, you should study everything you can about it.

 
**Some good things to look into with Ruby are:**
* Concurrency and multithreading techniques 
* Tracking running processes
* When to use `nil`
* Converting base numbers


To help you make the most of Ruby, Educative has created two courses on Ruby for different skill levels, *(beginner)* **[Learn Ruby from Scratch](https://www.educative.io/courses/learn-ruby-from-scratch)** and *(more advanced)* **[Ruby Concurrency for Senior Engineering Interviews](https://www.educative.io/courses/ruby-concurrency-for-senior-engineering-interviews)**. These courses use hands-on practice and real-world examples to teach you how to use Ruby most effectively. 

*Happy learning!*

<br>


### Continue reading about Ruby and programming tips
* [Getting Started with Ruby on Rails: build a web app with Ruby](https://www.educative.io/blog/ruby-on-rails)
* [How to plan a coding project in 8 easy steps](https://www.educative.io/blog/how-to-plan-a-coding-project)
* [7 coding tools that will maximize your effectiveness](https://www.educative.io/blog/7-coding-tools-to-maximize-effectiveness)