You Should Update One Gem at a Time with Bundler. Here’s How.

[Update: added example of updating the sextant gem, which causes Rails to be updated as well.]

Hey Ruby developers,

When you run bundle update to update your gems, it updates all of them at once. If your app stops working or your tests start failing, it can be pretty hard to figure out which gem update broke it.

There are a couple of solutions I’ve seen people use to solve this. Neither of them is that great:

  1. Lock the versions numbers in your Gemfile. But hey, that’s a pain and it’s what Gemfile.lock is for. Locking the version numbers in the Gemfile should be the exception, not the rule.
  2. Run bundle update gemname.

You might think bundle update gemname would just update that gem. But no, it also updates the gem’s dependencies—whether they have to be updated or not.  In fact, updating a third party gem can even upgrade you to a new version of Rails behind your back.

Here’s what the Bundler doc says about bundle update gemname:

UPDATING A LIST OF GEMS

Sometimes, you want to update a single gem in the Gemfile(5), and leave the rest of the gems that you specified locked to the versions in the Gemfile.lock.

For instance, in the scenario above, imagine that nokogiri releases version 1.4.4, and you want to update it without updating Rails and all of its dependencies. To do this, run bundle update nokogiri.

Bundler will update nokogiri and any of its dependencies, but leave alone Rails and its dependencies.

Yeah, sure, that sounds nice. But read that last sentence again: “Bundler will update nokogiri and any of its dependencies.” Well, heck. Some gems depend on a ton of other gems. And why not? Nothing wrong with that. And it makes sense to update the dependencies if something new is required for the update to work. But Bundler updates them no matter what. And now you’re back to the problem I stated at the beginning: even if you intend to update only a single gem, you still end up updating a whole boatload of gems. So when your app breaks or your tests fail, it takes a lot of time to figure out why.

Want an example of an unexpected side-effect of bundle update? I have a good one. Let’s say you’ve installed the sextant gem into your Rails app so you can see your Rails routes in development mode by navigating to /rails/routes. (It saves time compared to rake routes since the environment is already loaded.) In this example you are on Rails 3.2.2 and sextant 0.1.2. Now you run bundle update sextant to update to 0.1.3. Do you know what you just did? You upgraded Rails from 3.2.2 to 3.2.6. I don’t know about you, but I don’t like having my version of Rails updated just because I got the latest version of some little helpful gem.

When writing this blog post I tried a few scenarios. I found something else interesting:

With bundle update gemnameeven if there is no newer version of that gem, it will still update everything the gem depends on.

Here’s an example. My app has version 0.3.4 of haml-rails, which at the moment is the newest version. I run:

bundle update haml-rails

After that, git diff informs me that my Gemfile.lock now has newer versions of the journey, json, multi_json, and sprockets gems. Even though it didn’t find an update to haml-rails.

The Solution: bundle update ––source gemname

Bundler has a solution, but in my opinion it’s hard to understand the documentation.

Here’s what you do:

bundle update ––source gemname

I started diving into Bundler’s code and specs to see exactly what this does but it was taking more time than I wanted to spend. But I’ve been using this for more than a year and it works great. I recommend this be your default way to update a gem. If it doesn’t work due to a dependency conflict with other gems then you can always fall back on bundle update gemname.

As far as I can tell, using ––source is the equivalent of the following, but without all the work and headache:

  1. Specifying version numbers for everything in your Gemfile.
  2. When you want to update a gem, running gem list -r gemname to find out its latest version number.
  3. Changing the version number in your Gemfile for just that one gem.
  4. Running bundle install.
If you look at bundle update‘s documentation for ––source, it doesn’t help much. Here’s what it says:

OPTIONS

––source=<name>

The name of a :git or :path source used in the Gemfile(5). For instance, with a :git source of http://github.com/rails/rails.git, you would call bundle update ––source rails

By the way, the bundle update documentation does mention conservatively updating dependencies. Here is what it has to say: “For more information, see the CONSERVATIVE UPDATING section of bundle install(1) bundle-install.1.html.” The bundle install section on conservative updating does indeed describe how to update dependencies conservatively. However, this section assumes you are specifying version numbers in your Gemfile. As I mentioned above, I prefer to do that only as an exception.

See ya.

9 thoughts on “You Should Update One Gem at a Time with Bundler. Here’s How.”

  1. It’s maybe a little embarrassing, but the reason that this usage of `–source` isn’t documented is that I had no idea that was possible. :) If you’re interested in adapting the contents of this blog post into a section in the `bundle update` docs, that would be awesome.

    That said, the Bundler team _strongly_ recommends that you specify a version for Rails in particular. Rails doesn’t follow semantic versioning, and can make API-breaking changes in any version update. For that reason, putting a specific Rails version in your Gemfile is good practice, as well as documentation: this application works with this Rails version.

  2. It’s maybe a little embarrassing, but the reason that this usage of `–source` isn’t documented is that I had no idea that was possible. :) If you’re interested in adapting the contents of this blog post into a section in the `bundle update` docs, that would be awesome.

    That said, the Bundler team _strongly_ recommends that you specify a version for Rails in particular. Rails doesn’t follow semantic versioning, and can make API-breaking changes in any version update. For that reason, putting a specific Rails version in your Gemfile is good practice, as well as documentation: this application works with this Rails version.

  3. Thanks for the comment, André. I’ve been meaning to get in touch with you to ask if the docs could be updated. I would be happy to attempt an update, with the strong caveat that I don’t really know what the `–source` option does. All I know is what I described in this post, and that it does work better for updating a minimal set of gems.

    Regarding Rails, I agree with you and the Bundler team about putting a specific version in the Gemfile. I do that too.

  4. A note to any that copies and pastes. The dashes used in this blog post are not the same ones that would by typed in terminal “bundle update ––source gemname” are not the same as “bundle update –source gemname”

  5. THANK YOU!!! You’ve helped my whole team. I was mystified as to why it almost seemed to ignore the argument passed.

Leave a Reply

Your email address will not be published. Required fields are marked *

Feel free to use <a>, <b>, <i>, <strong>, <em>, <strike>, <code>.

Code blocks:
[code language="ruby/javascript/html/css/sass/bash"]
[/code]