Mastering Linters : A Code Quality Assurance Comprehensive Guide using Ruby on Rails
Tags: ruby, rails, tutorial, beginners
Published: 2023-11-08 12:15:52

As developers, we all come from different backgrounds, each with our own coding style. When working on personal projects, we have the freedom to express ourselves in any way we like—using functions with approximate names, methods with 40 lines of code... However, when collaborating on a collective project, conflicts often arise due to varying coding styles. This is where we introduce coding conventions, and our best friend for enforcing these conventions is the **linter**.

In this article, we'll dive into the world of linters, explaining their essential role in every development project. We'll also explore a powerful tool called **Lefthook**, designed to simplify the management of linters and other code quality-related tasks in our workflows. Finally, I'll introduce a selection of linters that I use daily, which have a significant impact on the quality of our code.

## **What Exactly Is a Linter?**

A linter is a tool designed to help developers maintain **clean** and **consistent** code. It does this by statically analyzing a project's source code to identify issues. These issues can range from simple syntax errors to subtle logic errors. Additionally, linters check if the code adheres to the defined project standards.

The strength of linters lies in their customizability. If a rule doesn't suit your needs, you can choose to override it.

Linters are versatile tools capable of detecting a wide range of minor issues. Examples of common formatting issues include extra spaces, lines that are too long, unused variables, and unknown references.

As a result, linters provide peace of mind to developers by helping them avoid common errors. You'll no longer have to spend three hours searching for the missing semicolon!

## My List of Linters for Rails Projects

In all my Rails projects, I aim to establish consistency in code formatting. Let's explore the linters I use on a daily basis!

### [Rubocop](https://github.com/rubocop/rubocop)

`Rubocop` is THE tool for Ruby. It focuses on adhering to the conventions established by the Ruby community. It checks elements like indentation, line length, the use of undefined variables, and more. Its goal is to make Ruby code as readable and consistent as possible.

To use it in your project, it's as simple as:

```bash
bin/bundle exec rubocop .
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/frjdsqo0d6fdinf1bzz5.png)

`Rubocop` is powerful because it also acts as a code formatter. By using the `-A` option, it will automatically correct simple issues like excessive line breaks.

---

### **[i18n-tasks](https://github.com/glebm/i18n-tasks)**

In projects that use I18n, I introduce `i18n-tasks`. It identifies untranslated strings in translation files and unused translations.

This tool allows me to identify translation gaps in my applications, making it incredibly useful when managing multiple languages in your applications.

To use it, it's as straightforward as:

```bash
bin/bundle i18n-tasks health
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2yrhpzlesmw3x1wt036y.png)

But `i18n-tasks` does much more:

- `i18n-tasks find` helps find all usages of a translation key in your code.
- `i18n-tasks add-missing` fills in missing translation keys in your translation files with a placeholder.
- `i18n-tasks translate-missing` takes it a step further by creating and translating all missing keys, with the option to use Deepl, Google Translate, and others.

`i18n-tasks` offers numerous features to simplify your translation key management. It's a gem that's worth exploring!

---

### **[Reek](https://github.com/troessner/reek)**

`Reek` is your best friend for detecting what we call "code smells." Code smells are a set of indications that show your code is tightly coupled. I'll write a more in-depth article on this, but the key takeaway is that tightly coupled code is challenging to maintain and evolve.

Once installed, you can simply use it like this:

```bash
bin/bundle exec reek app
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mw3rpsvxm34wd93p6anv.png)

Let's explore some examples of errors that `reek` can detect:

- **Duplicate Method Call**: When you call the same method multiple times with the same arguments.
- **Boolean Parameter**: When you pass a boolean argument, implying there are two execution paths in your method. This is a strong indicator of coupling.
- **Missing Safe Method**: Every "dangerous" method (ending with `!`) should have a corresponding "safe" method.

---

### [Brakeman](https://github.com/presidentbeef/brakeman)

`brakeman` is a security linter specifically designed for Ruby on Rails. It searches for potential security vulnerabilities in the code, such as Cross-Site Scripting (XSS) or SQL injection vulnerabilities. Using Brakeman is essential for maintaining a Rails application that is less vulnerable to security issues.

Once installed, you can run `brakeman` with:

```bash
bin/bundle exec brakeman -z -q
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0ehs0o6vv3qi6vxcefkq.png)

Here are some errors that `brakeman` can identify:

- **Cross-Site Scripting**: When you execute code that can be manipulated by the user, for example, through unfiltered params.
- **Dangerous Send**: Using `.send` can be dangerous if you don't validate the data passed as a parameter.
- **Unscoped Find**: When you search for a record directly through the model instead of the object that owns it.

---

### **[Rails Best Practices](https://github.com/flyerhzm/rails_best_practices)**

`Rails Best Practices` is a tool that focuses on Ruby on Rails projects. It identifies violations of Rails-specific best practices, contributing to project consistency and helping avoid common pitfalls.

To use it:

```bash
bin/bundle exec rails_best_practices .
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/htyfxwt77615i00ydens.png)

All the rules of this linter are available on [https://rails-bestpractices.com](https://rails-bestpractices.com/). While many of these rules are old, dating back to 2013, the majority of them are still applicable and relevant today.

Here are examples of rules that `rails_best_practices` checks:

- **default_scope is Evil**: Avoid using `default_scope` as it cannot be overridden.
- **Tell, don’t Ask**: This well-known programming principle encourages providing instructions or expressing actions directly rather than querying an object about its state to make decisions.
- **Restrict auto-generated routes**: When you use `resources` for your routes, Rails generates all CRUD routes by default. However, many times, not all of these generated routes are used.

---

### **[Haml-lint](https://github.com/sds/haml-lint) / [Erb-lint](https://github.com/Shopify/erb-lint) / [Slim-lint](https://github.com/sds/slim-lint)**

Your views also need to be consistent! It's essential to have a linter for any template syntax. Here, we'll focus on `haml-lint`.

Let's see how `haml-lint` works:

```bash
bin/bundle exec haml-lint app/views
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yeywfwcw7deurspwszgg.png)

The rules enforced by this linter ensure that your views are consistent and uniform.

Here are examples of rules that `haml-lint` checks:

- **Implicit Div**: Avoid creating unnecessary div elements.
- **Alt Text**: All images must have the `alt` tag.
- **Repeated Id**: Ensure that an ID is not used multiple times in the view.

### **[Scss-lint](https://github.com/sds/scss-lint) / [Css-lint](https://github.com/jacobbednarz/css-lint)**

Scss-lint is specifically designed for the Sass/SCSS language. It identifies syntax, indentation, and style issues in your stylesheets, helping maintain clean and organized CSS code.

Once installed, you can run it with:

```bash
bin/bundle exec scss-lint app/assets/stylesheets/
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0jfj0df336ywclfrb4wk.png)

Here are examples of rules enforced by `scss-lint`:

- **Zero Unit**: Do not use units after a 0.
- **BEM Depth**: Ensure compliance with the BEM (Block Element Modifier) naming convention.
- **Property Units**: Verify the use of the correct measurement units for attributes.

---

## Setting Up Git Hooks with Lefthook

Having all these linters is great, but it places a significant mental burden on developers to run them manually every time. Fortunately, **git hooks** exist!

In practice, a git hook is an action that executes when you use Git commands. For example, you can say, "Run this linter before I commit." This is incredibly powerful!

The problem? Git hook syntax is not very developer-friendly. However, for every problem, there's a solution, and let me introduce you to **Lefthook**.

Lefthook is a tool that manages your Git hooks, exposing a YAML file to orchestrate your Git hooks. It's available as a Ruby gem and an NPM package.

Let's see how to easily set up Lefthook in your Rails project.

Once you've installed the gem with `bundle add lefthook`, you'll create a `lefthook.yml` file at the root of your project. In this file, you'll specify all the linters you want to run with every Git action.

Here's a simple example of a `lefthook.yml` that sets up `rubocop`:

```yaml
pre-commit:
  commands:
    rubocop-linter:
      glob: "{*.rb,*.rake,*.jbuilder,*.builder,*.ru,Capfile,Gemfile,Rakefile}"
      files: git diff --name-only master
      run: bundle exec rubocop --force-exclusion {files} | grep "no offenses"
      skip:
        - merge
        - rebase
```

The list of instructions I provide to `lefthook` is straightforward. Before each commit, I run `rubocop` on all the files I've edited on my local branch. If there's an issue with `rubocop`, the commit fails.

To enable the Git hook, simply install it on your machine with the following command:

```bash
bin/bundle exec lefthook install
```

And there you go! The Git hooks are installed on your machine!

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k8q7gkz3moqjiehnvg22.png)

Keep in mind that the example we've seen is simple. In larger projects, you'll want to run all the linters we discussed earlier.

## Conclusion

Linters are more than just static code analysis tools. They are the silent guardians of code quality, contributing to more stable, readable, and efficient projects. By combining linters with Lefthook, you can automate code quality monitoring in your project while reducing human errors.

Clean code is not a luxury but a necessity. Linters and automation tools like Lefthook are essential parts of the modern developer's toolkit. So, don't hesitate to integrate these tools into your development workflow and reap the benefits they offer for code quality and project robustness.