Never type `bundle exec` again
43rd month of 4th era 2012-10-19 (?) · 798 words · 4 min

It is a profoundly erroneous truism, repeated by all copy-books and by eminent people when they are making speeches, that we should cultivate the habit of thinking of what we are doing. The precise opposite is the case. Civilization advances by extending the number of important operations which we can perform without thinking about them. Operations of thought are like cavalry charges in a battle — they are strictly limited in number, they require fresh horses, and must only be made at decisive moments.

—Alfred North Whitehead, An Introduction to Mathematics

There's a lot to be said about the failure of the computer revolution to be revolutionary and the failure of personal computers to be personal, and I won't get into it here.

But at the very least, I believe that professional programmers should be able to program their working environment to save themselves effort.

Specifically, professional programmers of Ruby should not have to write bundle exec before every goddamn thing.

If you don't write Ruby, then let me explain

For those who are

Step 1: direnv

Install direnv globally. Any method works. I use nix.

$ nix-env --install direnv
# ...
$ direnv version
2.20.0

Step 2: ~/.bash_profile

Complete your direnv installation by adding its hooks for your shell.

$ echo 'eval "$(direnv hook bash)"' >> ~/.bash_profile
$ source ~/.bash_profile

If you use zsh or fish, obviously you'd change the argument to direnv hook and add the line to a different file.

Step 2: .envrc

Add an .envrc file to the root of your project with the line layout ruby in it, and enable it by running direnv allow while in that directory.

This will set two environment variables that control Bundlers behavior and add two directories to your PATH environment variable.

$ echo 'layout ruby' >> .envrc
direnv: error .envrc is blocked. Run `direnv allow` to approve its content.
$ direnv allow
direnv: loading .envrc
direnv: export +BUNDLE_BIN +GEM_HOME ~PATH
nick-mbp% echo $BUNDLE_BIN
/Users/nick/Source/ruby_project/.direnv/bin
$ echo $GEM_HOME
/Users/nick/Source/ruby_project/.direnv/ruby
nick-mbp% echo $PATH
/Users/nick/Source/ruby_project/.direnv/bin:/Users/nick/Source/ruby_project/.direnv/ruby/bin # ...

GEM_HOME determines where bundler and other tools place gems.

BUNDLE_BIN determines where bundler puts binstubs.

PATH of course determines where your shell will look for commands you invoke.

Step 3: .gitignore

Bundler will be filling that .direnv directory, so let's leave it out of source control.

$ echo '/.direnv/' >> .gitignore

Step 4: bundle install

You know this one.

$ bundle install
# ...
Bundle complete! 25 Gemfile dependencies, 131 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.

Step 5: there is no step 5

Step 4 has put the binstubs of all your project's dependencies in .direnv/bin, and step 2 has put that folder at the front of your PATH. That means that when you run foo, it will always be the version of foo specified in the Gemfile.lock.

% which rake
/Users/nick/Source/ruby_project/.direnv/bin/rake
$ rake --version
rake, version 11.3.0
$ cd ..
direnv: unloading
$ which rake
/usr/bin/rake
$ rake --version
rake, version 12.3.2

Step 6: Reflection

I wrote all this so you could enjoy a reliable experience in at least one corner of your professional life.

I also hope that you start demanding it in all corners of your professional life.

The highest degree of reliability is not consistency, but invisibility. We often call something that "always" "works" reliable, without asking how much time we spend making it work, how much time we spent learning to make it work, and what probabilities are really hiding behind the blithe word "always."

You deserve better than that. Your life is worth more than the tedious things you've been trained to spend it doing and thinking about. You deserve things so reliable that you can use them without thinking about them.

It's not really possible to "save time." Time is always "spent," and can't be hoarded for later. But the reason we should fight to save each other time is so that we can spend it better. As in the story of the magic washing machine, you don't buy a washing machine to wash clothes, but to do things instead of washing clothes.

It's possible to get enjoyment from washing clothes or typing "bundle exec," but hopefully you can think of things you'd prefer to get enjoyment from.


now · effectiveness · philosophy · coding · home