The letter A styled as Alchemists logo. lchemists
Published August 15, 2022 Updated September 30, 2023
Cover
Ruby Code Quality

Code quality is always a top priority for me as a software engineer. This goes for any language, not only Ruby. That said, I want to focus on to ensuring you have the best in class when it comes to tools and practices for keeping your code in top shape. Let’s jump in!

Quick Start

Should you want to follow along locally, two gems that will configure your environment and set you up for success are:

  • Rubysmith: For generating Ruby projects. Run gem install rubysmith && rubysmith --build demo to get started.

  • Gemsmith: For generating Ruby gems. Run gem install gemsmith && gemsmith --build demo to get started.

Using either of the above will allow you to experiment as you read through the rest of this article. Next up, we’ll discuss the specific gems you’ll want to focus on.

Gems

At a minimum, these are the only gems you need:

I’ll walk you through and detail each next.

Git Lint

Git Lint is your code buddy for writing thoughtful commit messages that not only explain what you are architecting but also why the changes are important. You can wire Git Lint up as a local Git Hook, part of your Continuous Integration (CI) process, or as a GitHub Action. Personally, I use Git Lint as both a Git Hook and as part of my CI process. While Git Lint is written in Ruby, it is not specific to Ruby projects and can be used for any project of any language that uses Git as a source code repository.

What I love most about Git Lint is that it encourages me to use a Git Rebase workflow with a strong focus on writing good commit messages that tell a compelling story on how the architecture evolved. If I can’t explain myself, then that’s a red flag which means I haven’t broken down my implementation to be more clear. Often times, I’ll find a better solution to the problem which makes what I’m working on easier to read and maintain over time.

This also leads to code reviews that are enjoyable to read and learn from. I don’t want anyone, reviewing my code, to have to laboriously decipher my work in order to understand it. I’d rather the process be enjoyable where I can teach something new as well as show how I solve technical challenges. The same is true for me as a reviewer, I’m always eager to learn from others especially when I can read through each commit — like a page in a book — by starting with the first commit and finishing the story once I get to the last commit. At least — when using GitHub — I can hit the n key to turn to the next commit or hit the p key to turn to the previous commit in the code review. Again, very much like reading a book.

Lastly, there are a few, non-obvious, benefits from using Git Lint:

  • Interviews: One of the first things I like to check for is if someone is a good communicator. If they can’t explain what they are doing and why their work is important then that is an immediate red flag. Using Git Lint within these initial collaborations highlights these concerns quickly because, if you ignore them, they’ll resurface when it comes to assigning issues to work on, organizational skills, collaboration with others — especially code review feedback — and if they can deliver well thought out solutions.

  • Git History: Git Lint ensures the entire team has a Git History that is linear, atomic, and well documented. This information is a treasure trove of invaluable information when it comes to debugging an issue, understanding work by other team members which might be out sick or on vacation, or simply reloading the stack of thoughts of your own work back into your head.

  • Searchability: With a clean Git history, Git Lint helps ensure you can search through previous work quickly. Tools like git log --grep and git log -S become way more important with this level of quality.

Caliber

Caliber is code formatter and linter built upon the RuboCop stack. Caliber wraps many of the RuboCop gems you need for each project you work on. Even better, Caliber takes an aggressive stance to staying up-to-date with the latest developments within the RuboCop community — since new cops and changes are being introduced often — so project maintenance is less work.

Finally, Caliber encourages a rigorous approach to writing Ruby code which pays dividends when it comes to project maintenance. Here’s a quick breakdown of the similarities and differences between the Caliber and RuboCop gems:

Caliber RuboCop

Strict

Opinionated

Advanced Ruby

Deduplicates Cop Configurations

RuboCop Performance (included)

RuboCop Thread Safety (included)

RuboCop Rake (included)

RuboCop RSpec (included)

Reek

Reek is a powerful code analyzer and perfect companion to Caliber for detecting code smells which leads to improved design of your architecture and simplified maintenance of your project over the long run.

Reek, much like Caliber, has a strong focus on advanced techniques and patterns. In fact, Reek is so good that I feel I can’t do my best work — or enjoy the work I’m doing — unless Reek is part of the project I’m working on. There have been many times where Reek detected a code smell that I was able to clean up for an improved design that sped up code review feedback and helped me produce an implementation that was much easier to work with.

If I had a couple complaints, I wish Reek had better defaults for ignoring folders that shouldn’t be analyzed. You’ll not want your tmp and vendor folders scanned by Reek (if you have them). Thankfully, this can be corrected by adding a .reek.yml to your project with the following contents:

exclude_paths:
  - tmp
  - vendor

Reek also doesn’t detect, like RuboCop does, when an excluded violation is resolved. This mean you’ll need to clean up your configuration file from time to time by deleting unused configurations.

Reek, like Git Lint, makes a great interviewing tool too because if someone doesn’t know how to refactor code or prevent code smells in the first place then you have another measure for your interviewing rubric.

Simple Cov

SimpleCov wires into your test suite and provides statistics on the overall test coverage of your implementation. For example, here’s a screenshot of total coverage for the HTTP Fake gem:

Cover

With the above report, a score of 100% is shown. Had there been gaps in lines or branches (i.e. conditionals) covered, a score of less than 100% would have shown. In situations where issues are detected, you’d be able to click the affected lines to troubleshoot and resolve any issues.

There is a lot you can do with SimpleCov so I encourage you to read the documentation for further insight but — at a bare minimum — here’s what you can add to your RSpec spec_helper.rb for immediate use:

require "simplecov"

SimpleCov.start do
  # Ignores RSpec since only implementation coverage is important.
  add_filter %r(^/spec/)

  # Ensures specs for conditional logic is properly tested.
  enable_coverage :branch

  # Ensures code coverage for `eval` and/or `instance_eval` messages.
  enable_coverage_for_eval

  # Enforces a minimum of 95% for code coverage and fails if coverage drops below that threshold.
  minimum_coverage_by_file line: 95, branch: 95
end

The above will not only give you more confidence in your quality but also fail your build should the threshold fall below 95%. Perfect for preventing unnecessary code reviews since the author will have a checklist of items that can be fixed before initiating a code review which reduces unnecessary team notifications too.

Conclusion

All gems and configurations discussed above are provided by Rubysmith and Gemsmith — as mentioned in the Quick Start section earlier — so I hope you experimented with the Ruby projects generated by those gems while you were reading through this article and getting familiar with the various code quality analyzers.

In the end, I hope you are able to walk away with new knowledge on how to level up your skills even further. The best part is you don’t need a lot of tooling to automate this process. All you need is the rigor and fortitude to stick with this so project maintenance is low and you can remain focused on delivering quality features to your customers. 🎉