Bundler, Gemfile, and Gemfile.lock
Fast recall
Quick revision
Core answer
Gemfile declares an application's direct gem dependencies, sources, groups, and allowed version ranges. Bundler resolves those requirements together with transitive dependencies and records the selected versions and platforms in Gemfile.lock. Later bundle install runs reuse the lockfile so development, CI, and deployment use the tested dependency graph. Ruby applications normally commit both files; reusable gems generally declare runtime dependencies in the gemspec and often do not publish a lockfile for consumers.Whether you know the Gemfile is intent (constraints) and the lock is the resolved reality (exact versions), and the workflow (add, bundle install, commit both).
Committing the Gemfile without Gemfile.lock (others resolve differently), or editing the lock by hand (Bundler owns it).
Gemfile = intent (gem + constraint); Gemfile.lock = resolved exact versions. Add to Gemfile, bundle install, commit BOTH, run with bundle exec.
Interview-ready guide
Interview answer
Bundler creates a consistent gem environment for a Ruby application. The Gemfile is the human-maintained declaration: it names direct gems, their sources, optional version requirements, and groups such as development or test. Those lines do not list every transitive dependency the application will load.
When Bundler resolves the bundle, it finds one compatible version for every direct and transitive gem across the supported platforms. It writes that snapshot to Gemfile.lock, including exact versions, dependency relationships, platforms, and the Bundler version that last changed the lockfile.
For example, the Gemfile may allow rack, ~> 3.1. If the lockfile records Rack 3.1.12, a normal bundle install on CI installs the locked graph rather than silently selecting a newer allowed Rack release. An intentional update recalculates the relevant part of the graph and produces a reviewable lockfile diff.
Ruby applications should normally commit both Gemfile and Gemfile.lock so production runs the versions tested in development and CI. The .bundle directory is machine-specific and should not be committed. A reusable gem puts consumer dependencies in its gemspec; its development lockfile has different trade-offs because downstream applications perform their own resolution.
The Gemfile therefore expresses policy and the lockfile records one solved result. Bundler connects them by installing and activating that exact compatible set, preventing whichever gem happens to be globally installed from deciding application behaviour.
Bundler, Gemfile, and Gemfile.lock
Direct Answer
The Gemfile declares an application's direct gem dependencies, sources, groups, and allowed version ranges. Bundler resolves those requirements together with transitive dependencies and records the selected versions and platforms in Gemfile.lock. Later bundle install runs reuse the lockfile so development, CI, and deployment use the tested dependency graph. Ruby applications normally commit both files; reusable gems generally declare runtime dependencies in the gemspec and often do not publish a lockfile for consumers.
Deep Dive & Architecture
Dependency resolution is a graph problem. A Gemfile entry for Rails leads to requirements on Active Support, Active Record, Rack, and many more gems, and each of those brings constraints of its own. Bundler must select one version of each gem that satisfies the combined graph for the selected platforms.
The lockfile preserves the successful solution. It distinguishes direct dependencies from resolved specifications and records sources and platforms. Two developers with the same application lockfile can install the same graph even when newer compatible releases appear after the file was created.
A changed Gemfile does not mean every dependency should move. A normal install tries to preserve locked versions while satisfying the new declaration. A targeted update deliberately unlocks a selected gem and the dependencies that must move with it. Reviewing the lockfile shows whether the change stayed within the intended area.
Groups influence which gems are installed or automatically required in an environment, but Bundler still resolves a single coherent graph. It cannot safely choose one version of the same gem for development and another for production inside one lockfile. Consistency is the feature the bundle is protecting.
Code Example
source 'https://rubygems.org'
ruby '3.3.6'
gem 'rails', '~> 8.0.0'
gem 'pg', '>= 1.5', '< 2.0'
group :development, :test do
gem 'rspec-rails', require: false
end
# Commit Gemfile and Gemfile.lock for an application.
# Do not edit Gemfile.lock by hand; let Bundler resolve it.
Common Mistakes & Pitfalls
Committing the Gemfile without Gemfile.lock (others resolve differently), or editing the lock by hand (Bundler owns it).
Check your understanding
Answer these without looking back at the article.
- Why can exact versions in only the Gemfile still miss transitive dependencies?
- Should an application commit `Gemfile.lock`?
- Why does Bundler resolve one gem version across all groups?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.