With Bundler, why did `bundle update rack` also change other gems in Gemfile.lock, and what do `--conservative`, `--patch` and `--strict` change?
answer
- named gem unlocks its dependencies too
- shared dependencies move by default
- --conservative: only named gems unlock
- --patch/--minor reorder preference
- --strict turns preference into a limit
basics
~20 sbundle update rack unlocks rack and all of its dependencies, even ones other gems share, so they can move. --conservative unlocks only the named gems; --patch or --minor prefer smaller bumps; --strict forbids going past that level.
solid answer
~40 sBy default, naming a gem on `bundle update` unlocks that gem plus every gem it depends on in the lockfile, including dependencies another gem also uses, and resolves them to the newest versions the Gemfile allows - so a lockfile diff can show several gems moving. `--conservative` unlocks only the gems you name and keeps their dependencies at locked versions where possible, which may leave the named gem lower. `--patch` and `--minor` only change the *order of preference* among allowed versions, so a larger bump can still happen if resolution needs it; `--major` is the default. Adding `--strict` removes versions beyond the chosen level entirely. Gemfile constraints always apply first, so `~> 1.0` already acts like `--minor --strict` for that gem.
code
bash · 8 lines# rack plus all its dependencies, shared ones included
bundle update rack
# only rack; its dependencies stay at locked versions where possible
bundle update rack --conservative
# every gem, but never past its next patch release
bundle update --all --patch --strictgo deeper
Recall that bundle update with a gem name can move that gem's dependencies as well, so the lockfile diff may show more than one gem.
Explain the unlock set: named gem plus its dependencies, shared ones included, versus --conservative; and that --patch and --minor reorder preference while --strict limits.
Choose the right blast radius for a security patch or a routine upgrade, and read the resulting lockfile diff to confirm nothing outside the intended subgraph moved.
Set the team's upgrade policy: patch-only strict sweeps versus broad updates, how often each runs, and how much transitive churn a single change may carry.
## Why a targeted update moves more than one gem `bundle update rack` reads as "update rack", but Bundler's rule is broader: it **unlocks the named gem and every gem it depends on** in the locked graph, then resolves that subset against the newest versions the Gemfile allows. Every other gem keeps its locked version. The `bundle-update(1)` man page spells out the surprising part. If `thin` and `rack-perftools-profiler` both depend on `rack`, then `bundle update thin` updates `thin`'s dependencies - `rack` included - even though `rack` is **also** a dependency of the gem you did not name. A dependency the named gem shares with others is unlocked too. So a diff for a "one gem" update can legitimately show: - the named gem moving; - its direct and indirect dependencies moving; - nothing else moving - gems outside that subgraph stay put. ## `--conservative`: unlock only the names `--conservative` applies the same behaviour `bundle install` uses after a Gemfile edit: **only the gems you list are unlocked**, and their dependencies stay at locked versions where possible. The man page describes it as "do not allow indirect dependencies to be updated". The trade-off is that the named gem may not reach its newest release, because a newer release may require a dependency version that is still locked. That is often what you want for an urgent patch: move one gem, touch nothing else, and keep the review small. ## Patch-level preference: `--patch`, `--minor`, `--major` When Bundler picks among allowed versions it normally sorts them newest first. The patch-level options change that **order**: | Option | Preference for `foo` locked at 1.0.2, with 1.0.3, 1.0.4, 1.1.0, 1.1.1, 2.0.0 available | |---|---| | `--major` (default) | 2.0.0, 1.1.1, 1.1.0, 1.0.4, 1.0.3, 1.0.2 | | `--minor` | 1.1.1, 1.1.0, 1.0.4, 1.0.3, 1.0.2, 2.0.0 | | `--patch` | 1.0.4, 1.0.3, 1.0.2, 1.1.1, 1.1.0, 2.0.0 | Because these are **preferences**, a version outside the level can still be chosen if that is the only way to satisfy the graph. The man page's own example shows `bundle update --patch` moving a transitive gem up a minor version because the new patch release of its parent requires it. ## `--strict`: from preference to limit `--strict` removes every version beyond the chosen level from consideration: 1. `--patch --strict` for the example above leaves 1.0.4, 1.0.3 and 1.0.2, so `foo` goes to 1.0.4. 2. `--minor --strict` leaves up to 1.1.1. 3. If the graph cannot be satisfied within the limit, the gem moves less or not at all rather than jumping past it. The Gemfile still comes first. A requirement of `~> 1.0` already excludes 2.0.0, which the man page notes is the same as `--minor --strict` for that gem. ## A worked example with a transitive gem The `bundle-update(1)` man page uses a Gemfile with only `gem 'foo'`, a lockfile holding `foo 1.4.3` and `bar 2.0.3`, and these releases: `foo 1.4.4` needs `bar ~> 2.0`, `foo 1.4.5` and `1.5.0` need `bar ~> 2.1`, and `foo 1.5.1` needs `bar ~> 3.0`. | Command | Result | |---|---| | `bundle update --patch` | foo 1.4.5, bar 2.1.1 | | `bundle update --minor` | foo 1.5.1, bar 3.0.0 | | `bundle update --minor --strict` | foo 1.5.0, bar 2.1.1 | | `bundle update --patch --strict` | foo 1.4.4, bar 2.0.4 | Two lessons follow. Under plain `--patch`, `bar` still makes a minor jump because the preferred `foo` needs it. And `bar`, which the Gemfile never names, is free to move because it is not a declared dependency; adding `--strict` keeps its move inside the level as well. ## Putting it together - **Routine upgrade of one gem:** `bundle update rack` - accept that its dependencies move and review the diff. - **Security patch with minimal churn:** `bundle update rack --conservative --patch`. - **Bulk safe upgrade:** `bundle update --all --patch --strict` for patch releases only. - **Everything in a group:** `bundle update --group test`. The same flags exist on `bundle lock --update` when you want the lockfile change without installing. Whatever the flags, commit the resulting Gemfile.lock so every machine gets the new versions through a plain `bundle install`.
- Why can `bundle update --patch` still move a gem up a minor version?`--patch` only reorders preference among allowed versions. If the newest patch release of a gem requires a newer minor release of a dependency, Bundler accepts that to satisfy the graph. Add `--strict` to forbid versions beyond the patch level, at the cost of some gems not moving at all.
- When is `--conservative` the wrong choice?When the named gem's new release needs newer dependencies. Holding those at locked versions can leave the named gem short of the release you wanted, such as the one carrying a fix. Then a plain targeted update, which also moves the needed dependencies, is the honest choice, reviewed through the lockfile diff.
saying these in an interview costs you the question
- bundle update rack moves rack only; its dependencies always stay locked
- --patch guarantees no gem moves past a patch release
- --conservative stops Gemfile.lock from changing, like frozen mode
- --major lets Bundler ignore the Gemfile's version constraints
- A shared dependency is never unlocked by a targeted update