Sanely Updating Your Gems

Update: Derek Prior offers much better advice on how to manage your Gemfile, so you probably want to read his article instead.

If you followed my advice in the previous post, your Gemfile would look something like this:

source :rubygems
 
gem 'rails', '3.0.3'
gem 'devise', '1.1.5'
gem 'redgreen', '1.2.2'
gem 'capybara', '0.4.0'

There’s nothing wrong with this except that if you wanted to keep your gems up to date frequently it would be tedious to manually change all of these versions. Fortunately, we don’t always have to be this exact with the version numbers.

Version Constraints

Here are examples of the most useful version constraints:

gem 'foo'             # use any version
gem 'foo', '1.1.3'    # use version 1.1.3
gem 'foo', '>=2.3.2'  # use any version 2.3.2 or later
gem 'foo', '>=1.2'    # use any version 1.2.0 or later
gem 'foo', '~>3.0.4'  # use any version >= 3.0.4 and < 3.1.0
gem 'foo', '~>2.8.6'  # use any version >= 2.8.6 and < 2.9.0
gem 'foo', '~>4.2'    # use any version >= 4.2 and < 5.0
gem 'foo', '~>0.5'    # use any version >= 0.5 and < 1.0

The ~> operator is called the pessimistic operator or the spermy operator. You can read more about it in the RubyGems docs.

Now we can type:

$ bundle update

and it will update all of our gems with the latest gem versions, respecting the version constraints we have in place.

Rational Versioning Policy

All RubyGems should follow the Rational Versioning Policy. In a nutshell, this says:

  • Gem versions have three numbers. (2.4.2, 0.4.7, etc.)
  • The first number should change if the gem’s interface changes in a way that breaks backwards-compatibility.
  • The second number should change if the gem simply adds new capabilities to its interface.
  • The third number should change when the gem only changes its inner implementation details.

This means that if you use version 2 of gem foo, any future version 2 would not break your code. Version 3 might, though. So if gem authors stick to the Rational Versioning Policy, you can safely specify versions like so:

source :rubygems
 
gem 'rails', '3.0.3'
gem 'devise', '~>1.1'
gem 'redgreen', '~>1.2'
gem 'capybara', '~>0.4'

without fear of your code breaking when you update the gems.

Note that Rails is too complex to follow the Rational Versioning Policy, so we should always specify an exact version of Rails.

You can skip to the end and leave a response. Pinging is currently not allowed.

5 Responses to “Sanely Updating Your Gems”

  1. shir says:

    Thanks. Nice to know about Rational Versioning Policy

  2. Derek Prior says:

    Until I have a specific reason to include one, I leave version constraints out of my gemfile altogether. Other developers on my projects that run `bundle install` will get the same versions I installed thanks to gemfile.lock. To update gems, you have to run bundle update. Thats a concious decision and at that point, I rely on my test coverage and QA plan to catch bugs.

    If I do find and incompatibility then it’s simple enough to add the restraint, re-run bundle and re-test. Then I can add a note to the gemfile indicating what broke with the newer version.

    Why lock your gemfile down?

  3. techiferous says:

    Derek,

    It all depends how much risk you’re comfortable with. For many projects, your approach is good enough. Don’t worry about problems until they come up. However, if you are in a situation where application bugs can be costly, it’s worth managing your gem versions with care. That’s because Gemfile.lock does not specify the exact versions of gems, so when the application code gets installed on another machine that already has gems present, the resulting gem versions can be different. It can get especially tricky with dependent gems. One developer can have a different version of a dependent gem than another, which can cause different behavior.

  4. Derek Prior says:

    Your gemfile.lock file does indeed include the EXACT version of all of your direct dependencies *AND* their dependencies. It’s the gemfile.lock file – and not your gemfile – that guarantees that every subsequent run of `bundle install` results in the same exact gem versions. Your gemfile doesn’t contain any information whatsoever about the dependencies of your dependencies.

    The only reason I can see to lock down your gemfile (in the absence of a known problem with a specific gem) is if you fear someone running `bundle update` and pushing code to the live site without running your tests and following proper QA or if you simply don’t have adequate tests or qa.

    Bundler has been around a while now, but it seems like it’s not really well understood.

  5. techiferous says:

    Thanks, Derek! I dug a little deeper and I stand corrected. You’re right–Gemfile.lock does specify the exact versions of the gems installed.

Leave a Reply