Building a Better Gemfile

Posted August 8, 2013 by Taylor Fausak

If you've been hacking on a Rails project for a while, chances are your Gemfile has spiraled out of control. For instance, the main OrgSync Gemfile contains 127 gems. That's a ton of dependencies, and they slow us down. Running bundle exec rake environment takes at least 15 seconds. That might not sound like much, but it adds up. Think of it as a 15 second tax every time you do anything.

Plus, we don't specify versions for many of the gems. This makes running bundle update downright dangerous, as you could upgrade to a new major version of a gem. Gems in the asset group, like CoffeeScript and Sass, were the worst offenders. There's a comment right above them that says:

Make sure you perform a full asset precompile on deploy whenever you update any of the gems in the following group.

But none of them have any versions specified. You could argue that that's what Gemfile.lock is for. I don't buy that. There's a world of difference between gem 'sass' and gem 'sass', '~> 3.2.10'.

What can be done?

With one simple helper function, you can fix both problems. Drop this into your Gemfile before any gems:

def gem(name, version, options = {})
    if options.has_key?(:require)
      if options[:require].equal?(true)
      options.merge!(require: false)

    super(name, version, options)

Happier Developers with Versions

By overwriting the gem function and requiring a version parameter, you can be sure you'll never end up with a gem with an unspecified version. If you try to, bundle will blow up at you:

.../Gemfile:3:in `gem': wrong number of arguments

This has the benefit of making bundle update much less risky to run. Also, it's a lot easier to tell at a glance which versions of gems your project needs.

Faster Startup with require: false

Instead of eagerly requiring all of your gems, the modified gem function doesn't require them unless you set require: true. As a result, most of your gems won't be available any more. You'll have to explicitly require them at the top of the file.

As a Pythonista, this doesn't bug me one bit. After all, explicit is better than implicit. If you still need convincing, consider this: It got the OrgSync project's startup time down to 12 seconds (a 20% improvement). And that's not even as fast as it could be — 43 gems are still eagerly required.

[Originally posted to my blog.]

Taylor Fausak was born in California, but he got to Texas as soon as he could. He studied Computer Science at the University of Texas at Austin before entering the wild world of software development. After a brief stint at Cisco, he started his career at Famigo working on all aspects of web development. Then he swapped his Django experience for a chunky slice of Rails bacon and joined OrgSync in the fall of 2012. When he's not slinging code around, he likes riding bikes, playing Magic, and throwing frisbees.

comments powered by Disqus