The application skeleton generated by
rails new contains a bit of code that I think you’re better off removing:
For the uninitiated,
Bundler.require loops over each gem in your
Gemfile and requires all gem entries that have not explicitly disabled it with an option like
:require => false.
Bundler.require is certainly convenient (you can use any gem from anywhere in your application, without having to think about it!), but it has a handful of significant downsides.
Here are 5 reasons to avoid
1. It slows down your application start-up time
When you use
Bundler.require, it slows down the start-up time for anything that boots your application environment. Requiring files is not a free operation. Listing rake tasks with
rake -T gets slow. It takes much longer to get test feedback from the very first test, even if the test itself is very fast.
The worst part is that your application’s start-up time will grow linearly with the number of gems you are using. Each time you add a gem to your
Gemfile, it adds to the start-up time for each and every command you run that boots your environment. Do you really want to pay that kind of tax?
Compare how long it takes to list the rake tasks in an application that uses
Bundler.require compared to one that doesn’t. This is real output from two ruby applications at SEOmoz. One uses
Bundler.require and one doesn’t:
That’s nearly 50 times faster.
And really, if you think about it, it’s kind of ridiculous: why should a command that lists all available tasks (but runs none) waste time loading every gem your application uses? Likewise, why should running a single test file for an
ActiveRecord model load gems that are only used by controllers?
2. It removes
require as a source of design feedback
Once you stop using
Bundler.require, you have to start explicitly requiring your gems. I think that many ruby programmers dislike doing so because Rails itself tends to discourage explicit requires. Rails’ constant autoloading allows you to use any class in your application from anywhere without needing to require it, and the generated
config/application.rb file uses
Bundler.require so that you do not have to require any gems.
However, in an application that uses explicit requires at the top of each file, they serve a useful purpose: you can tell at a glance how many dependencies a class has. They are a great source of design feedback. When a class needs 20
require statements to work, it suggests that it’s probably coupled to too many things. You may want to split out some classes from the monolith.
3. It makes your code harder to understand
Having a list of requires at the top of your files makes your code easier to understand, too.
Consider this code:
You come across this bit of code, and notice the use of the
String#shellescape method. Having never seen it before, you’re curious about it and visit the ruby string docs but can’t find any mention of it.
Imagine if this was the code instead:
The presence of a
require statement gives you a great hint where to go look for the source of a method.
shellwords in the ruby standard library defines
String#shellescape, but without the
require statement, there’s no easy way to guess this.
4. It will continue to load code long after it is no longer used
There’s another nice benefit of explicit
require statements: when you remove the last file that requires a particular gem, the gem will no longer be loaded at runtime. This will reduce your application’s memory footprint.
In contrast, consider what happens when you use
Bundler.require and you remove the last file that relies upon a certain gem: nothing. The gem will continue to be loaded at runtime, adding bloat to your application processes. You can of course remove the gem’s entry from your Gemfile, but in a large application, you may not know if a particular gem is still being used…especially since the lack of explicit
require statements gives you no easy way to tell.
5. It prevents you from controlling the gem load order
(Update: I was wrong on this point, actually. Bundler maintainer Andre Arko was kind enough to correct me below.)
There's one final downside to
gives you no way to control the order the gems are loaded.
Usually this doesn't matter...but occasionally it does, and
it can lead to surprising, hard-to-track-down bugs.
Consider a gem like
It hooks into many different HTTP client libraries and provides
a consistent API to stub HTTP requests. Before version 1.7.0,
it would use constant detection to figure out whether or not
it should hook into a particular HTTP client library. For example,
it would hook into curb if and only if the
Curb constant was
defined when WebMock got loaded. Usually, things worked just
fine, but occasionally curb/webmock users would find that it wasn't
working as expected, due to the fact that WebMock got loaded
Yehuda Katz has rightfully
that gem authors not use constant detection to decide whether or not to
activate some functionality, and I fully agree with that advice.
However, as a gem user, you may wind up using a gem that uses this
technique, and if you rely on
Bundler.require to load your gems,
you have no way to ensure the gems are loaded in the correct order.
If you use explicit
require statements, it is trivial to
Using Bundler without Bundler.require
It may sound like I dislike bundler, but I think bundler is a fantastic tool, and I use it in all my projects. I just don’t use
In addition, I recommend you explicitly require each gem you rely on in each file that requires it, rather than loading them all from some file that is run at boot-time. This avoids the downsides of
Bundler.require while gaining the benefits I’ve outlined above from using explicit
In effect, this uses bundler to solve the hard problem it is really good at (i.e. resolving and locking dependencies) but avoids using bundler for the really easy problem that you’re better off managing yourself (i.e. requiring each gem).
Building Rails apps without Bundler.require
If I’ve convinced you to avoid using
Bundler.require, you may be excited to go remove the
Bundler.require line in your rails app’s
config/application.rb file. Unfortunately, it’s not that simple :(.
For one, in an existing rails application, this is almost certainly going to break things. For another, many gems (particularly those that provide a railtie) assume that users will be using
Bundler.require. Many gems don’t provide clear instructions for how to use their gem if you do not use
Let’s tackle the first problem first. If you’re starting a new rails application, I recommend you remove
Bundler.require from the start. Then there’s no migration pain :). If you’re working on an existing application, you’ll need to replace your
Bundler.require statement with a list of
require statements, one for each gem. Over time, you’ll want to slim this list down to just the gems that are actually needed at boot time (hopefully a small list), but in the meantime, listing them all should (hopefully, at least) keep your application working.
As for the second problem, there isn’t a “one size fits all” solution. You may have to dig into the source code for the gems you use to understand how they work a bit better–but this is probably a good thing, anyway! I can give you an example of the sort of thing you’ll need to do, though.
Many gems provide rake tasks. If the gem is no longer being loaded at environment boot time, these tasks may not be available. Ripple, for example, provides a handful of rake tasks. To make these tasks available in your application, you’ll need to add
load "ripple/railties/ripple.rake" to your
Rakefile, which is essentially what the
rake_tasks block in Ripple’s railtie does.
When you use
Bundler.require, you are opting for convenience over sustainable application development. It takes a bit more work (and greater understanding of how your application’s gems plug into your application framework) to use explicit
require statements, but I’ve found that doing so pays off in spades.
For your next or current application, consider using explicit
require statements rather than