Every development team has their own preferences for what kind of checks they want to run on newly committed code. One specific flavor of tools that developers use is static analysis. Static analysis tools are programs that preform checks “statically” on code. This means that they determine the correctness or validity of code without executing it.
In nonprogramming terms, static analysis is a lot like checking a sentence for errors before speaking it out loud. There is a wide range of problems and insights that static analysis can discover. However, since these tools don’t execute our code, they can’t catch any error that’s unique to the execution runtime. In the same way that we check sentences for grammatical errors, some problems don’t become apparent until we speak (or execute) the words in sequence.
Today, I want to run through an example using static analysis on a Ruby on Rails project and integrating those checks into our Codeship CI process.
To accomplish this, we’ll need a few tools:
A source-control connected version of a Ruby on Rails application like this
We’ll start developing our CI process locally and then look to translating it to a Codeship configuration.
Staying in Style with Rubocop
Rubocop is the first Ruby static analysis tool I want to explore. While there are a variety of purposes that this library covers, I’ve found it's most commonly used as a means to enforce the Ruby Style Guide in your project.
There’s a lot to be said about having a consistent style across your application. Sandi Metz wrote a really good blog about it recently. One of Sandi’s points that resonated with me was:
Code is read many more times than it is written, which means that the ultimate cost of code is in its reading.
In larger teams of developers, having them create the same kind of consistently readable code is crucial for efficient collaboration. Rubocop helps remind us how our code fits a specific coding style.
We can install Rubocop via:
gem install rubocop
This will give you the ability to run the Rubocop CLI by simply running:
rubocop app
Rubocop will then run some checks that will either come back successful or with errors. We can treat no errors as a “successful” check and a run with any errors as an “unsuccessful” check.
In a sense, our testing and audit process might eventually look something like this:
bundle exec rspec spec # or whatever testing framework you use rubocop app
As mentioned before, please note that if Rubocop finds any error in its analysis, the build will fail. This might be somewhat annoying as your test suite and code base grows. However, you can configure Rubocop’s preferred ruleset with a .rubocop.yml
file.
Having a consistent style and formatting is certainly nice. However, I’d love to talk a bit about some static analysis libraries that dive a bit deeper than just checking for consistent style.
Detecting Code Smells with Reek
Next we’re going to talk about Reek, a static analysis tool that detects code smells in your application.
If you’re not familiar with the idea of code smells, Jeff Atwood (@codinghorror) has an excellent rundown of them. I’ve found Reek to be incredibly useful for helping me understand if my newly created code exhibits some of these smells.
We can install Reek via:
gem install reek
With Reek installed, we can run an audit on our codebase locally. I like to target specific aspects of my application for code smells. Because of this, I tend to just run Reek on my app
directory.
You can execute this audit by running:
reek app
Reek will then run some checks that will either come back successful or with errors. We can treat no errors as a “successful” check and a run with any errors as an “unsuccessful” check.
In a sense, our testing and audit process might eventually look something like this:
bundle exec rspec spec # or whatever testing framework you use reek app
As mentioned before, please note that if Reek finds any error in its analysis, the build will fail. This might be somewhat annoying as your test suite and code base grows. However, you can configure Reek’s preferred ruleset with a .reek
file.
We’ve talked about using Rubocop to make our style consistent and Reek to make our code smell better. However, how do we implement these kind of checks on Codeship?
!Sign up for a free Codeship Account
Codeship Setup
To kick things off, log into your Codeship account and navigate to Create New Project
. You’ll then need to connect your Source Control provider of choice and point Codeship toward your hosted project.
Once everything is connected, you’ll be presented with the Test
deployment pipeline setup. Depending on how complex your CI pipeline is, you may just want to write a custom solution. However, I’ve found that selecting the Ruby on Rails
option as a starting point is incredibly helpful.
Our project will have a Test
pipeline that looks something like the following:
Setup Commands
rvm use 2.4.0 # Or whatever Ruby version you're using bundle install gem install reek # Used to execute Reek gem install rubocop # Used to execute Rubocop rake db:create rake db:migrate
Our setup commands help prepare our application for testing, but we’re also installing Reek and Rubocop outside of our application bundle. This is because we’ll be executing them outside of Bundler in our next step.
Test Commands
bundle exec rspec spec rubocop app reek app
With these elements all in place, we now have a CI pipeline that allows us to test and audit our application code with every new push.
Looking to the Future
Using static analysis tools for local development is incredibly useful for getting more information on the code you’ve written. However, adding static analysis checks into your pipeline is even more of a commitment. You might find yourself becoming frustrated by certain failures and smells as your codebase grows. Yet the benefit of having somewhat of a “cleaner” codebase will benefit you immensely in the future.