Tips to improve the design of your Ruby code
Tags: ruby, programming, tips
Published: 2018-09-21 00:55:14

---
title: Tips to improve the design of your Ruby code
published: true
description: Make your code more readable and easier to understante following these design code tips.
tags: [ruby, programming, tips]
---

![image](https://thepracticaldev.s3.amazonaws.com/i/t8js64ppcouwqpv1fs3i.jpg)

> Any fool can write code that a computer can understand. Good programmers write code that humans can understand - Martin Fowler.

### Why you should care about the design of your code?

As developers, we sometimes tend to think that being productive is related to deliver your task as fast as possible, but for me, that is not the truth.

Be a productive developer is far from just execute your task and deploy your code, is also related to that code quality, how easy to maintain and evolve that feature in the next releases. Because of this, it's important to treat your code, even the minimal changes with attention.

Imagine that you work for a company that has software with a huge source code, too many features, too many teams, different approaches. Today you're working in a  determinate feature, next week you focus is another, two weeks from now you return for the first feature... when you work for a big company, this flow is kind of normal.

Always remember, sometimes, you need to prevent your self from your own legacy old.

### Avoid hidden structures

  - The code is hard to understand and hard to maintain
  - You must avoid use index position directly to get some data from an array

An example to avoid:

```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([[622, 20], [622, 23], [559, 30], [559, 40]])
```

The main problem of this code is that it is using the indexes positions of the array to get some information to be used inside de `calculate` method, but we can not be sure of what are these pieces of information because `0` and `1` are not some meaningful.

#### How to avoid hidden structures?

  - Use constants to assign values

```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
```

Assigning the indexes positions in a constant will help to improve the code because now, the indexes are meaningful.

  - Use a struct to create a class

```ruby
Shape = Struct.new(:height, :width)

class RevealingReferences
  def initialize(data)
    @data = prepare(data)
  end

  def calculate
    data.collect do |shape|
      shape.height + (shape.width * 2)
    end
  end

  private

  attr_reader :data

  def prepare(data)
    data.collect do |value|
      Shape.new(*value)
    end
  end
end
```

Another approach is to use `Struct` data structure to represent the data.

### Avoid comparison with regular expressions

  - Instead of it, assign this regular expression to a meaningful variable name, and use this variable.

```ruby
  # Instead of using directly the regular expression in the code...
  if string =~ /\d+/
    # ...CODE
  end

  # Assign it to a variable with a meaningful name, and use it.
  is_decimal = /\d+/
  if string =~ is_decimal
    # ...CODE
  end
```

This simple change could save your time trying to understand what the regular expression is doing.

### Avoid directly dependencies

  - Rather use [Composition over Inheritance](https://www.thoughtworks.com/insights/blog/composition-vs-inheritance-how-choose)
  - Embrace [SOLID Principles](https://robots.thoughtbot.com/back-to-basics-solid)

### Follow the Demeter's Law

  - Avoid high coupled code
  - Do not know about the dependencies from a dependency

```ruby
class Customer
  def state
    address.state.name
  end
end
``` 

In the example above, the `Customer#state` method violates the Demeter's Law, because of the fact that, to access the `state.name` we first need to the `customer` to get in contact with your relation with `address` and then the relationship with `address` will contact with `state`.

So we can conclude that the class `Customer` knows too much about your dependencies. Any error inside the `State` class can break the `Customer`, even they are not directly related.

An approach to solve this problem is using [Delegators](https://ruby-doc.org/stdlib-2.1.0/libdoc/delegate/rdoc/Delegator.html).

### Tell don' t Ask!

  - Tell the objects what to do, do not ask if the can do
  - Avoid asking an object which is your current state, to perform something based on this answer
  
```ruby
class SocialMediaPost
  def send
    SocialMediaPost::API.post(args)
  end

  def connected?
    connected
  end
end

post = SocialMediaPost.new(content: 'Some Message')

if post.connected?
  post.send
end
```

In the code, above we first ask to the object if it's connected and then based on this we perform an action to `post.send`. 

Now imagine that you need to execute this actions in different points inside your code base and now, you need to add another verification in addition to the current one. Besides, with this code the internal details of the `SocialMediaPost` are exposed, we know that `connected?` must be true to perform other methods.

- Do not expose the details of an object unnecessarily 

```ruby
class SocialMediaPost
  def send
    SocialMediaPost::API.post(args) if connected?
  end

  private

  def connected?
    connected
  end
end

post = SocialMediaPost.new(content: 'Some Message')
post.send
```

This approach will not expose the internal details of the class and follows that Demeter's Law.

### Conclusions

  - Boy scout rule: Always leave your code better than you found it
  - A better design starts on the small changes
  - You need to write code for humans, not for computers