It’s surprising how quickly time can get away from us. Updated versions of Ruby gems are released all the time. If we don’t keep our applications up-to-date with the latest released versions of their dependencies, we may end up with applications that rely on gems with known vulnerabilities, bugs, or deprecated features.
I use the following strategy on a regular basis to keep my applications up-to-date, and to make Rails upgrades much easier.
Bundler to the rescue
There are many ways of keeping our gems up-to-date. Gemnasium is a great tool for letting you know about gem updates, and even updating them for you automatically.
Security issues are a particularly clear reason for upgrading a gem. bundler-audit is a tool for checking your gems versions against a database of known security vulnerabilities. We use this in many of our projects and we’ve recommended it in the past.
A very useful tool is built into
outdated. It lists installed gems
with newer versions available. Running
bundle outdated in a project will
produce a report like this:
% bundle outdated Fetching gem metadata from https://rubygems.org/........... Fetching version metadata from https://rubygems.org/.. Fetching dependency metadata from https://rubygems.org/. Resolving dependencies.... Outdated gems included in the bundle: * inline_svg (newest 0.11.1, installed 0.9.0) in group "default" * jquery-rails (newest 4.2.2, installed 4.2.1) in group "default" * sprockets (newest 3.7.1, installed 3.7.0) in group "default" * capybara (newest 2.11.0, installed 2.7.1) * nokogiri (newest 1.7.0, installed 22.214.171.124) * sass (newest 3.4.23, installed 3.4.22)
This report is from a client project of ours that is less than one month old. As you can see, it’s already time to update some gems.
Updating gems one at a time
Of course, whenever we update a dependency, we run the risk of introducing bugs or unexpected side-effects. So, exercise caution.
A good technique is to upgrade one gem at a time, run tests, and then create a commit. This can take a while to do manually, but by doing this ourselves we have a chance to review release notes and think about the updates we’re making one at a time, instead of updating everything all at once. Lee Pender has a gem called bummr which can help to automate much of this process.
Fix any deprecation warnings or breaking changes from these dependency updates
in the same commit that updates the gem. The commit message should include a
link to the release notes for the gem, and any notes related to the update. If a
bug is introduced, it’s much easier to figure out which gem update was the
culprit if each gem update is in a separate commit. We can leverage
run, for example.
Here’s a pull-request I recently made to update various gems on a client project:
You’ll notice the PR description has useful links to release notes, and each commit in the PR shows the change in version of each gem.
Upgrading Rails is so much easier
It’s good practice to keep dependencies up-to-date. When a new version of Rails is promoted as a release candidate, there are usually a slew of updates to various gems that resolve compatibility problems or start to fix deprecation warnings. The path to upgrading to a new Rails is much easier and more straightforward if we update our dependencies first.
By upgrading these first and fixing our applications to work with any breaking changes in the new gems and then resolving any deprecations, upgrading to the new Rails version becomes an exercise confined to changes in Rails itself instead of trying to figure out if problems or warnings we’re seeing in our logs and tests are from changes in Rails or both Rails and our dependencies.
A good habit
By updating your gems early, you can avoid getting very far behind. The more often you update your gems, the less work it will be each time. Why not get into the habit of upgrading your gems on a regular basis, perhaps weekly or monthly?