Rubocop: Getting to Green in a Huge Rails App

Dmitry Tsepelev
Power the People
Published in
4 min readJan 23, 2018

--

Motivation

Rubocop is a commonly used tool for checking Ruby code style. There are two main style guides — for Ruby and for Rails, and it covers both of them. The idea is fairy simple: you install Rubocop gem and the plugin for your favourite editor and it shows you all the problems just in place. You can also turn it on at the CI in a way that it will comment your PRs (hello, Pronto!) or even fail your build if it can find any rule violations.

There are a lot of benefits from following the guidelines:

  1. Most of the Ruby developers are already following these rules
  2. It makes code more readable
  3. It makes code review faster and easier
  4. It can show you possible performance issues (e.g. you should use tr instead of gsub when you want to replace a single character in the string)

First run

When you start a brand new project you can start writing the code right after setting up Rubocop. Config can always be changed later but at the beginning you can start with the default one (who knows, probably you will be able to keep default method length rule?).

However, when you have a big project and run rubocop for a first time you will see something like this:

$ rubocop --format offences11010 Metrics/LineLength
3140 Style/StringLiterals
2201 Style/FrozenStringLiteralComment
1891 Style/Documentation
…and thousands of rare offences
1 Style/ZeroLengthPredicate

26154 Total

And again: 26154 Total. It can take some time to fix that amount, right? Not really.

How to configure Rubocop

We are going to put our custom configuration to the .rubocop.yml file. That’s the perfect moment to ask your team for a help, because it’s hard to keep code style when people ignore some rules because they don’t like them. When everyone agrees that rule is not needed — you can disable it. In the meantime we can fix some offences immediately.

First of all, let’s think about the code we don’t want to fix at all: migrations, generated files, etc. When you have that list you can Rubocop to stop checking them:

AllCops:
Exclude:
— db/schema.rb
— db/migrate/**/*
— lib/tasks/**/*

Secondly, think about your specs and rules you can ignore for now. For instance, do you really want to fix the length of lines? If not — you can disable that check in a following way in the specs directory (I strongly recommend to check the offences report for the specs directory):

Metrics/LineLength:
Max: 120
Exclude:
- spec/**/*

Thirdly, let’s go through the list of the most popular offences, for instance, Metrics/LineLength (means that we have a lot of long (> 80 chars) lines, what if we make the amount bigger?) or Style/Documentation (if you are not going to document your code you can disable it right now).

After that, you can take a look at the list of offences again and check if Rubocop can fix them automatically (in order to do that you will need to re-run it with the special flag). It can cause some tricky merges so be careful.

Finally, if you can’t wait to fix a lot of real issues found by Rubocop you can fix them right now! But I bet there are some more (maybe couple of thousands) of offends you still have to fix before you can turn CI on.

How to make CI pass

What if we could ask Rubocop to ignore our current offences for now but do not allow to introduce more?

We can do that! All we need to do is to run $ rubocop --auto-gen-config which will create a new file called .rubocop_todo.yml. This config adds “exclude” to some rules when there are not so many files offended, otherwise it will change rule boundaries (e.g. it can set Max to 500 in Metrics/LineLength) or even disable some rules.

For some reason the output of the command suggests to add inherit_from: .rubocop_todo.yml to .rubocop.yml. Don't do it! It will suspend all the warnings in the local environment.Instead of it let’s add inherit_from: .rubocop.yml to .rubocop_todo.yml and tell the CI to use this file as a config: $ rubocop --config ./rubocop_todo.yml

As a result, our CI is green but it will fail if we offend any rule in a new or not ignored file. As soon as we fix things from the TODO list we can either remove lines from the TODO config or move them to the general one, but it can be done later.

Recap

As a conclusion I’d like to reiterate all the steps:

  1. Configure Rubocop in a way that every person in the team likes (or at least not strongly opposed to) it
  2. Try to fix as many offences as possible but ask your coworkers about the big features they are working on — there might happen a tricky merge couple of days later!
  3. Generate the TODO-config ($ rubocop -c ./rubocop_todo.yml), and do not forget to inherit in from the base one
  4. Configure your CI to run rubocop with TODO config and check if it works fine
  5. After that you’ll end up with the project which still has some style issues which we decided to ignore, but passes the style check. Now we can remove rules from the TODO one by one and fix the remaining offences

--

--