Gems and Bundler
Ruby's package ecosystem -- installing gems, and using Bundler to keep a project's dependencies reproducible.
2 min read
A gem is Ruby's unit of packaged, shareable code — the equivalent of an npm package or a Python wheel. RubyGems.org hosts the public registry, and the gem command that ships with Ruby installs from it.
Installing a gem directly
gem install colorizerequire "colorize"
puts "Success!".green
puts "Warning!".yellowrequire loads a gem's code into your script, similar to import elsewhere. This works fine for quick scripts, but it doesn't track which version of a gem you installed, and a second machine running the same script might silently get a different, incompatible version. That's the problem Bundler solves.
Bundler and the Gemfile
Any real Ruby project — and every Rails app — uses Bundler to declare and lock dependencies. You list what you need in a Gemfile:
# Gemfile
source "https://rubygems.org"
gem "rails", "~> 7.1"
gem "pg"
gem "colorize", "~> 1.1"
group :development, :test do
gem "rspec-rails"
end~> 7.1 is a "pessimistic version constraint" — it allows 7.1.x and 7.2+ up to (but not including) 8.0, giving you patch and minor updates without risking a breaking major version bump. Grouping gems under :development, :test means they're only installed in those environments, not in production.
bundle installThis resolves every gem's dependencies, installs them, and — critically — writes a Gemfile.lock file recording the exact version of every gem (including transitive dependencies) that was installed.
Why the lock file matters
Gemfile.lock should always be committed to version control. It's what guarantees that running bundle install on a teammate's machine, or on your production server, installs the exact same versions you tested against locally — without it, "works on my machine" becomes a real and common problem as gems release new versions over time.
Running code with the right gems
bundle exec rspec
bundle exec rails serverbundle exec runs a command using exactly the gem versions locked in Gemfile.lock, rather than whatever version happens to be installed globally on your system. Skipping bundle exec is a common source of "it works when I run it directly but not through the test suite" confusion, especially on a machine with multiple projects using different gem versions.
Finding and evaluating gems
Before adding a gem to a project, it's worth checking a few things on its RubyGems or GitHub page: how recently it was updated, how many other projects depend on it, and whether its issue tracker shows active maintenance. A gem that hasn't been touched in years, or has open security advisories, is a real risk to inherit — every gem you add is code you're trusting to run inside your application.
The takeaway
For a throwaway script, gem install is fine. For anything you'll come back to, share with others, or deploy, use a Gemfile and Bundler from the start — retrofitting dependency management onto an already-growing project is far more painful than starting with it.
Test what you just learned
4 quick questions. Get all of them right to unlock the next lesson.
You can take the quiz without an account — logging in just lets your result count toward your progress.