Over the past couple of weeks, there’s been some conversation about Bundler and RubyGems and their place in the Ruby community.

Shopify, pulling strings at Ruby Central, forces Bundler and RubyGems takeover
Ruby Central recently took over a collection of open source projects from their maintainers without their consent.
Bundler belongs to the Ruby community
I’ve spent 15 years of my life working on Bundler. When I introduce myself, people say “oh, the Bundler guy?”, and I am forced to agree. I didn’t come up with the original idea for Bundler (that was Yehuda). I also didn’t work on the first six months worth of prototypes. That was all Carl and Yehuda together, back when “Carlhuda” was a super-prolific author of Ruby libraries, including most of the work to modularize Rails for version 3.

Taking a step back from the drama, I’m reminded of just how much Bundler has shaped not only Ruby development, but the entire world of modern programming.

Life Before Bundler

Before Bundler became common, keeping a project on a consistent set of gem versions took more manual work. RubyGems could install and activate specific versions, but coordinating the whole dependency graph across machines was still awkward.

The Bundler Revolution

Bundler changed that almost overnight. It introduced a simple, elegant model:

  • A Gemfile to declare what your project needs.
  • A Gemfile.lock to lock the exact dependency graph.
  • A project-specific environment, so apps stopped trampling over each other’s gems.

Committing a lockfile made it much easier for developers and CI to install the same gem versions. Reproducing an entire build still depends on the runtime, platform, system libraries, and other inputs.

It sounds obvious now, but it wasn’t at the time. Bundler unified ideas that had existed in various forms and made them the default. Once developers experienced that workflow, they never wanted to go back.

A Ripple Felt Everywhere

Similar dependency-management workflows are now familiar across languages, although their histories and guarantees differ:

  • Bundler: Gemfile declares dependencies and Gemfile.lock records their resolved versions.
  • npm: package-lock.json records the resolved dependency tree; npm-shrinkwrap.json serves a related purpose for published packages.
  • Cargo: Rust projects use Cargo.toml and Cargo.lock.
  • Mix/Hex: Elixir projects record resolved dependencies in mix.lock.
  • Go modules: go.mod records requirements used in version selection. go.sum contains integrity hashes, not a lockfile.
  • Python: Pipenv and Poetry provide lockfiles alongside their dependency declarations.

Bundler helped make a consistent, project-specific dependency environment an everyday part of Ruby development. That is a contribution I still appreciate every time I work on DocSpring.

Gratitude

Personally, I can’t imagine building DocSpring without Bundler. Our application depends on a complex set of libraries, and managing the dependency graph by hand with individual gem install commands would be a nightmare, if not downright impossible. Bundler lets me ship with confidence. What works in CI works in production, and upgrading dependencies is... easier than it would be without Bundler. (It can only do so much.)

I’m grateful to everyone who poured their time, energy, and care into Bundler over the years: Yehuda Katz, Carl Lerche, André Arko, and the 354 individuals who contributed to Bundler and to RubyGems. Their work has reshaped the daily experience of software developers everywhere.