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.
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
- Ruby has a ubiquitous package manager named Bundler.
- Bundler has a subcommand
exec
which runs commands in an environment where . - It's very
- You will probably not get much use from this article. You don't ever find yourself typing
bundle exec
, so a method to save yourself that effort isn't any help to you.
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.