Recently I came across situation when I spent really bad time resolving conflicts between gem version specified in Gemfile. This rails project had a Gemfile with all gem version specified in very strict way, for e.g. -
gem ‘rails’, ’3.0.3′
gem ‘devise’, ’1.1′
gem ‘omniauth’, ’0.1.6′
gem ‘mysql2′, ’0.2.6′
Showing above some of the gems from the Gemfile. I came across some bugs with currently used version of twitter gem while connecting to Twitter api’s. So I tried to upgrade twitter with
bundle update twitter
This was giving issue, that newer version of twitter required to have newer version of faraday gem. While bundler couldn’t install faraday, as omniauth was depending on this older version of faraday. I tried removing the version no. of omniauth from Gemfile, and ran ‘bundle update’. That way, bundler upgraded all gems and including omniauth, faraday and twitter and resolved all the dependency issues itself. This fixed old bugs with twitter, but my omniauth was now failing, as API for newer version of omniauth was changed.
So finally I posted a question over stackoverflow. This question generated some very good discussion but that didn’t cleared all the confusions with me as bundler solves many a problems like –
- Different systems should have same gem versions.
This is the requirement that all the developers should have same gem versions. Also development, production and staginging boxes should share same gem versions. This can be solved with Gemfile.lock file, and not by strictly declaring version numbers in Gemfile. One of blog post by Yehuda Katz explains this a bit, and encourages application developers to include Gemfile.lock in version control. I previously used Gemfile, without any version specification and it worked quite well as well, until one of the developer did ‘bundle update’. Running bundle update causes all gems to upgrade. This has high changes of breaking app, as newer versions of gem may have upgraded/changed/deprecated API.
- Application should stick to compatible gem version all the time.
This is to stay away from major upgrades to the gems, which come with gem API changes. These type of upgraded has potential to break application.
While searching a bit more about it, I found one good solutions – twiddle-waka.
Twiddle-waka can be said a pessimistic way of versioning. For example, a versioning should go as follows or as explained here –
Version 2.1.0 — Baseline
Version 2.2.0 — Introduced some new (backward compatible) features.
Version 2.2.1 — Removed some bugs
Version 2.2.2 — Streamlined your code
Version 2.3.0 — More new features (but still backwards compatible).
Version 3.0.0 — Reworked the interface. Code written to verion 2.x might not work.
For above gem, if one uses version 2.* while developing, and then upgrades to 3.*, app is supposed to broke. But with pessimistic versioning i.e. using twiddle-waka, safer version can be specified as ‘~> 2.2′. With this, app will be using something strictly below 3.0 while bundle having more options to resolve dependencies with 2.* versions.
The only issue with this way can be said as quoted from Dan Croak’s post -
I think most people are in favor of this in principle but are not consistent in practice. This leads to confusion: “Which libraries are consistent and which aren’t? Is this gem safe to declare as a dependency at the major, minor, or patch level?”
It seems like a simple way of clearing up the confusion is for authors to declare their intention of consistent versioning by using the twiddle wakka in their README’s installation instructions. If I see this…
gem “clearance”, “~> 0.9″
… then, I feel the author is assuring me of the “safety” of this gem at the minor level.
So I would say, strictly specifying gem versions in Gemfile is not a good way unless exceptionally needed. This way bundler lefts with very few options to resolve dependencies and hence too much version conflicts may arise.
Not specifying gem versions at all in a Gemfile is also not a good practice. This way, some bundle update ran by some developer may update all of your gems, causing app to break.
Hence, the middle way is twiddle-waka. Specify gem versions, but with safe version range. This way bundler can have options to resolve dependencies and that is great help.