Why Bundle exec? What’s the difference?

Ting Yi Shih (Peter Shih)
4 min readSep 7, 2023

As an iOS developer, I write ruby scripts too because of the common practices of CocoaPods and Fastlane. We’re often encouraged to run the ruby scripts with Bundle — but wait, what is that, and what’s the difference? How do I set this up with minimum effort?

This question has wandered in my mind and I just couldn’t easily find the answer. After some survey, I guess now I got mine. Taking notes here in case someone also needs them :-).

Gem

Before we dive into this topic, we’ll have to learn what gem is. A ruby package is called a gem. A gem can be distributed and installed. A gem can have its version and can evolve.

Gem is also the name of the program that manages the gems in your environment. You could have multiple versions of the same gem installed in your environment, but when you invoke the gem, you are only able to access the version decided by your search path.

# You are allowed to install multiple versions of the same gem
gem install cocoapods -v 1.0.0
gem install cocoapods -v 1.12.1

# You can look up which version you're invoking
pod --version

But boss, we’re in some trouble here. There are a variety of projects in the computer, each of which could require different versions of the same gem. That is, project A might need pod of 1.12.1 while a super legacy project B won’t work without pod of 1.0.0.

Bundler

Here comes the hero. Bundler was made as the solution to the gem versions. We maintain a Gemfile for each project, where we specify what version of a gem is needed.

# Gemfile in project A
gem 'cocoapods-check', '1.12.1'

Before we install the specified gems, we’ll have to install bundle the tool:

gem install bundler

From now on, we don’t manage the gems (in this project) with gem anymore. We use bundle. To install the required gems listed in Gemfile:

# To install the gems in Gemfile
bundle install

And to execute the gems specified by Gemfile:

# To execute the gems in Gemfile
bundle exec pod --version

You might now wonder the difference from calling pod --version directly. Only by calling the gem with bundle exec will we call the specified version in Gemfile. That is, calling bundle exec in different projects may result in different programs.

Here is a graph that hopefully illustrates what’s going on:

  • Gem versions required by Gemfile can be resolved by Bundler.
  • Without Bundler, OS just looks for the first version in the env path.

Until now, everything works great! 🎉 You are able to create different Gemfile’s for project A and project B without worrying about the colliding gem versions!

We just finished the basics of Bundler, but there are some insights here, which I view as the most valuable part during my exploration.

Bundler Installs Gems Globally

It’s important to note that Bundler installs gems globally by default. Unlike some package managers, Bundler doesn’t achieve version isolation by installing gems locally within the project. Instead, it works with globally installed gems of various versions, selecting the appropriate version specified in the Gemfile. Different projects asking for the same version of a gem share the same one.

Bundler won’t work without Gemfile

Even if the gem has been globally installed, you are not allowed to run it if the gem isn’t specified in the Gemfile. For example, in a project wherepod doesn’t appear in its Gemfile, running bundle exec pod --version leads to the failed to load command error.

The ‘vendors’ Directory Isn’t Always Necessary

Bundler offers a configurable option called BUNDLE_PATH, allowing you to change the location where gems are installed. This means the flexibility for you to prevent Bundler from installing gems globally. You can even set up separate .bundle/config files for different projects. Such projects may assign a directory named ‘vendors’ to host gems locally in the project directory, where ‘vendors’ could be added to .gitignore because it can be regenerated easily as long as you have BUNDLE_PATH set to it. However, there’s often no need because Bundler’s default behavior of keeping gems global has several advantages including shared gems among projects, reduced disk space usage, and cleaner project directories. An exception, though, is CI, where the ‘vendors’ has to be created locally for CI to cache and share across runs; that’s another story.

Reference: https://stackoverflow.com/a/32940896/4844397

If this article did any benefit to you, please kindly give a clap so I know I’m not the only one who suffered! Or if you have different opinions, also please leave a comment to let me know! :-)

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

Ting Yi Shih (Peter Shih)
Ting Yi Shih (Peter Shih)

Written by Ting Yi Shih (Peter Shih)

Love exploring an elegant pattern that forms robust, maintainable, and understandable coding style.

Responses (1)

Write a response