Register Now

Five Practices for Robust Ruby on Rails Applications


Reading Time: 3 minutes

This article was originally published on Brewhouse’s blog by Philippe Creux, and with his permission, we are sharing it here for Codeship readers.

Whenever I come across a Rails application with inconsistent data or bugs that are hard to nail down, I tell myself: “They (the developers) were just a couple of keystrokes away from preventing those issues from happening.”

At Brewhouse, we follow five simple practices to make our Rails applications robust. It all comes down to failing early, loudly, and often. We ensure that data is valid and applications behave properly by catching issues early on.

Use Hash#fetch to Catch Malformed Hash

unexpected method 'upcase' for nil… Whenever you expect a hash to contain a key, prefer fetch() over []. fetch(). This will raise an error when the key is missing so you won’t pass nil values around and see unrelated errors happening down the line.

Use case ... else raise to Catch Invalid Data

Always add an else raise ... clause to your case statements. You want to know when you’ve received an unexpected value rather than ignoring it and moving on.

Use Active Record ! Methods to Fail Loudly

Data is often seen as the most valuable asset in a company. Failing silently to persist data can have a huge impact. Whenever you’re not expecting an operation to fail, use the “bang” version of create!, update! and destroy! that raises exceptions on failure. This extra key stroke will save you from dealing with inconsistent data.

Used in test code, this ensures that the setup doesn’t fail silently. There is nothing worse than a test that passes because the setup was incorrect.

Also, always wrap multiple calls into an SQL transaction to prevent your data from getting to an in-between state.

Sign up for a free Codeship Account

Add Active Record Validations to Perform Live Checks

Pairing Active Record validations with the use of “bang methods” is a great way to ensure you persist valid data. For example:

class Post < ActiveRecord::Model
  validates :author, :blog, presence: true
  validates :published_by, presence: true, if: :published?
  validates :comment_count, numericality: { greater_or_equal_to: 0 }
  # ...

Use Database Constraints to Ensure Data Consistency

Your database is your best friend when it comes to ensuring data is present, not duplicated, and that orphans are not left in the database.

null: false

Given the fact that the large majority of columns are required in a database, you should define columns with null: false by default.

index ... unique: true

Did you know that Rails’ has_one does not prevent duplicate associations from being created?

class Account
  has_one :account_settings

account = Account.create!

 # => one of the three account settings you've created... -_-

The best way to prevent this from making data inconsistent is to add a unique index.

add_index :account_settings, :account_id, unique: true

The database will throw an error if you attempt to persist a duplicate record.


You don’t want orphan records in your database, do you? Foreign keys help such things from happening. I’d recommend using the Ruby gem schema_auto_foreign_keys to automatically add foreign keys on your behalf.

A Few Extra Keystrokes Go a Long Way

A few extra keystrokes here and there can save you from hours of debugging or recovering from inconsistent data. Use !, raise, validate, and database constraints. Your coworkers and your future self will thank you.

As always, feedback is greatly appreciated. I’d be happy to hear of any other practices I didn’t cover here.

Subscribe via Email

Over 60,000 people from companies like Netflix, Apple, Spotify and O'Reilly are reading our articles.
Subscribe to receive a weekly newsletter with articles around Continuous Integration, Docker, and software development best practices.

We promise that we won't spam you. You can unsubscribe any time.

Join the Discussion

Leave us some comments on what you think about this topic or if you like to add something.