As a newcomer to Rails, understanding Gems and the Gemfile is fairly straightforward: You include a gem that you need and you get it included everywhere.
We don’t often think about it,
but that last bit is important:
we are literally
all of the code from each gem
into our application, all the time.
There are other implications of
include behavior which should be considered,
but here I’m going to address one:
Gem Drift and the associated cost.
Let’s say I include an auth gem called
in my app to do authentication and authorization.
(Note: this is not a real gem, but a name I love.
Feel free to use it but please attribute here
so I can feel good about my gem-naming skills.)
Over time, the nice person who wrote
reviews issues and PRs at night and on weekends
and keeps development moving at a good pace.
She is a saint and deserves an award
or at least you should buy her a beer
if you run into her at conference.
Newly released gem versions are, for the most part, new features and security patches (particularly in auth libraries). Not keeping up-to-date with new features is no big deal in the short term, really. I mean, yes, they built a nice OAuth DSL but we don’t use that in our app, so who cares.
Not keeping up to date with security patches is a bigger issue. You know who keeps up with those updates? Bad people who want to target your innocent webapp.
You need to keep your app up to date, so how do you do it?
Anything you need to do routinely should be automated, right? You can use bundler-audit to regularly check for reported security fixes.
It works like this:
Add the gem:
# Gemfile # ... group :development, :test do gem 'bundler-audit', require: false end
Then set it up to check for vulnerabilities when you run the test suite:
# Rakefile # ... task default: [:spec] task default: ['bundler:audit']
# lib/tasks/bundler_audit.rake if Rails.env.development? || Rails.env.test? require 'bundler/audit/cli' namespace :bundler do desc 'Updates the ruby-advisory-db and runs audit' task :audit do %w(update check).each do |command| Bundler::Audit::CLI.start [command] end end end end
Be sure to add it to your CI as well.
Great! We’re covered, right?
Sorta. This will check the database of reported security issues which is some subset of released security patches. I don’t blame gem maintainers for this: again, they’re working in their spare time for the greater good. Expecting them to keep up to date with more admin activities isn’t really fair.
So what can we do?
A Path Forward
I started doing something on projects recently which colleagues either don’t care about or appreciate me doing:
I take 15 minutes
Then I open a PR with the changes, and hopefully deploy within the hour once I get code review.
The changes are very often quite minor and when they’re not, I look at the gem’s release notes and figure out what the changes are. If it’s a big change, say new deprecations or breaking changes, I fix them in about 30 minutes. People are pretty good about SemVer.
Remember software is best changed in small iterations, and updating gems is still shipping software changes.
“HAHA, oh great plan but we are way out of date already and can’t just jump right up to current.”
Of course. So here’s what you do:
Go into the lockfile (
and figure out what the specific versions
are of things which are really far out of date.
For example, let’s say
is now at version 2.0.7,
but we don’t know what we are locked at:
# Gemfile.lock # ... huge_bouncer (1.2.4) bcrypt email_validator (~> 1.4) rails (>= 3.1)
We can use the twiddle-waka operator to say “when updating this gem, only do so to the next major version,” like so:
# Gemfile gem 'huge_bouncer', '~> 1.2.4'
Okay so now we are not moving that much,
and can do a safe
without moving the versions too much.
In this case, let’s say we get up to 1.2.8.
This is an improvement!
Then, when we next have time,
we can loosen the restrictions a bit,
bundle update again,
and see if all is okay.
Remember, we want to make and deploy small changes, so don’t get all crazy.
Wait, What About Robots Again?
Shouldn’t you build or pay for some service which does this for you?
You are including a heck-ton of other code and you should be aware of how it is changing. Maybe not in minute detail, but it’s a lot of code which you are shipping, so you should read the label.
The Money Saved
No one will care if you update gems every Monday morning but you are saving your company lots of money.
I asked some friends to estimate the cost of getting totally current with their gems at their company or clients and here’s what they said:
- Upgrading Rails 2 to 3 is about 16+ developer-weeks, so about a $100-200k problem
- Upgrading Rails 3 to 4 is about half of that.
Assume the bigger the codebase, or the older it is, the higher the cost.
So no, it’s not glamorous work but someone should thank you for doing it.
Also, if you are up to date, others will pick up the slack for you because you’ve made it so easy. Good job! You are a minor hero!
Big thanks to Jason Draper for helpful edits and promising to argue with me about this in public.
Also, thanks to the people who shared estimates or anecdotes of what it costs to upgrade Gem suites.