The First Gem You Should Add to Your Ruby Project

Background

One of the first things new parents learn is the importance of establishing a healthy routine. Consistency helps the whole family maintain good habits, and predictability makes kids feel safe. When everyone knows what's coming next and what to do, the whole process runs smoothly.

This concept translates to software engineering as well. Code is read more often than it is written, so readability must be prioritized. Code written following consistent rules and patterns is more readable, predictable, and maintainable. When you know what to expect, and what's expected of you, you can focus your mental energy on the content of the code, as opposed to the way it's organized and formatted.

When I first started learning Ruby on my own, there wasn't an official Ruby style guide, and so I wrote Ruby based on the way I saw others write it. Which wasn't bad at all since I discovered great teachers along the way, such as Ryan Bates from Railscasts, Gregg Pollack from Code School (now Pluralsight), thoughtbot, Sandi Metz, Avdi Grimm, and various folks featured in the excellent Ruby Weekly newsletter.

But then I discovered RuboCop, a gem that enforces the rules in the community-driven Ruby Style Guide, and my Ruby code became a lot cleaner and more consistent. It was like having a more experienced person right there with me. I developed an interest in code quality and learned more about it through many of the same resources as mentioned above.

Once I started writing Ruby professionally, I noticed that the existing codebases I joined were not consistent. Thanks to my experience with RuboCop, I convinced my teammates of the importance of consistency, and that RuboCop would help us become and stay consistent. Over time, this resulted in a measurable improvement in our code quality. This led to leadership opportunities such as starting the Engineering Best Practices Guide at 18F, new internal working groups, and a Practices Guild.

Getting started with RuboCop

To follow along, you'll need a working Ruby development environment with Ruby 2.7.2, Bundler, and the ability to install gems without sudo. My script can set everything up for you in minutes.

In this guide, we'll keep it simple and work with a brand new Rails app. Adding RuboCop to an existing project with lots of code that doesn't adhere to the Ruby Style Guide is more involved, and deserves its own guide. Follow my steps for properly creating a new Rails app and creating the initial Git commit.

Once the app is created, we'll add RuboCop to the Gemfile, as well as several extensions: rubocop-performance, rubocop-rails and rubocop-rspec. We'll add all of these to the development and test group:

group :test, :development do
  gem 'rubocop', require: false
  gem 'rubocop-performance', require: false
  gem 'rubocop-rails', require: false
  gem 'rubocop-rspec', require: false
end

The next step is to install the gems:

bundle install

Then you'll need to tell RuboCop to use the extensions by creating a configuration file called .rubocop.yml at the root of your project's directory:

touch .rubocop.yml

Then paste the following into it:

require: 
  - rubocop-performance
  - rubocop-rails
  - rubocop-rspec

AllCops:
  UseCache: true
  NewCops: enable

Let's capture these changes in a Git commit. Future you will thank past you the next time you need to remember how to add RuboCop to a Rails app:

git add .
git commit -m "Add RuboCop gems and config file"

I recommend starting with the default settings, and then learning about offenses as they come up. Once you understand what RuboCop is complaining about and why, you can decide whether or not you want to ignore the offense, or choose an alternative if one is available. Let's see what kind of offenses exist in our brand new Rails app:

bundle exec rubocop

As of today, a new Rails 6.1 app with the above RuboCop configuration has 79 offenses, but most of them can be autocorrected. 30 of them have to do with using double quotes instead of single quotes, such as:

test/test_helper.rb:3:9: C: [Correctable] Style/StringLiterals: Prefer 
single-quoted strings when you don't need string interpolation or special 
symbols.
require "rails/test_help"
        ^^^^^^^^^^^^^^^^^

The description is clear (if you understand string interpolation and special symbols), but what's missing is the "why". All great command line programs have a help option, so let's see if there's a way to get more details about offenses:

bundle exec rubocop -h

Reading through the first few lines, we can see that the --only-guide-cops option will "Run only cops for rules that link to a style guide." Let's search for "style guide" in the terminal output. Aha! The -S flag will "Display style guide URLs in offense messages." Perfect! Let's give it a whirl:

bundle exec rubocop -S

Now we see that Style/StringLiterals links to https://rubystyle.guide#consistent-string-literals. Let's see what it says.

Adopt a consistent string literal quoting style. There are two popular styles in the Ruby community, both of which are considered good - single quotes by default and double quotes by default.

In this case, there are two acceptable options to choose from, and it comes down to preference. What matters is to pick one and stick with it. RuboCop defaults to single quotes, but you can configure your .rubocop.yml to use double quotes if you prefer them. The easiest way to find out how is to look up the cop's configuration:

bundle exec rubocop --show-cops Style/StringLiterals

Then copy and paste the output into your .rubocop.yml, and change single_quotes to double_quotes next to EnforcedStyle:

# Supports --auto-correct
Style/StringLiterals:
  Description: Checks if uses of quotes match the configured preference.
  StyleGuide: "#consistent-string-literals"
  Enabled: true
  VersionAdded: '0.9'
  VersionChanged: '0.36'
  EnforcedStyle: double_quotes
  SupportedStyles:
  - single_quotes
  - double_quotes
  ConsistentQuotesInMultiline: false

As we observed earlier from RuboCop's help, not all cops have links to the Ruby Style Guide. In that case, we can learn more by visiting the RuboCop Docs and searching for the cop name. One example is Layout/SpaceInsideArrayLiteralBrackets.

Once you've gone through all the offenses and learned more about them, and perhaps changed their configuration to your liking, then you can autocorrect most of them. Let's start with the ones that are safe to autocorrect:

bundle exec rubocop -a

Then let's commit these changes:

git add .
git commit -m "Safely autocorrect RuboCop offenses"

Now let's see just the names of the remaining offenses, and how many of each there are:

bundle exec rubocop -f o

The vast majority of the remaining ones are about Style/FrozenStringLiteralComment. This one is not safe to autocorrect because it could potentially break our app, but since this is a brand new Rails app, nothing is likely to break. Let's give it a shot, this time with the capital A flag. We'll also tell RuboCop to only autocorrect this specific offense. That way, if anything breaks, we can easily revert the change.

bundle exec rubocop -A --only Style/FrozenStringLiteralComment

Let's see if we can still run our app:

bin/rails s

Looks good. Now let's try running the Rails console:

bin/rails c

All good. If this were an existing app, you would also definitely want to run your tests to make sure nothing broke, but since there aren't any tests yet, we can go ahead and commit these changes:

git add .
git commit -m "Autocorrect FrozenStringLiteralComment offenses"

Now let's see what's left:

bundle exec rubocop -f o

Ha! We have 27 new ones about Layout/EmptyLineAfterMagicComment, which RuboCop introduced when it autocorrected the FrozenStringLiteralComment. Let's fix those:

bundle exec rubocop -a

And commit them:

git add .
git commit -m "Autocorrect Layout/EmptyLineAfterMagicComment"

Now we should be down to 11 offenses:

bundle exec rubocop -f o
4   Style/RedundantFetchBlock
3   Style/Documentation
2   Style/ClassAndModuleChildren
1   Rails/FilePath
1   Style/GlobalStdStream
--
11  Total

Now it's your turn to go through the remaining offenses and learn how to fix them or disable them. Then, as you start building your Rails app, keep running RuboCop, and over time, the best practices will come naturally. In a future guide, I will go over various ways to automate code quality checks so that you don't have to remember to run them manually before each commit.

I hope you enjoyed this introduction to RuboCop. There's more I haven't covered, so stay tuned for more posts, and in the meantime, I encourage you to read the documentation.