skip to content

What does Bundler's `bundle outdated` report, and how do `--strict`, `--filter-major` and `--only-explicit` narrow its output?

level: middleimportance: nice to knowfreq 24%

answer

  1. read-only: changes no file
  2. Current, Latest, Requested columns
  3. exit status 1 when anything is outdated
  4. strict: only what the Gemfile allows
  5. only-explicit: skip transitive gems

basics

~20 s

bundle outdated lists gems whose newer versions exist, with current, latest and requested versions, and changes no file. --strict shows only versions the Gemfile allows, --filter-major only major jumps, --only-explicit only Gemfile-declared gems. It exits 1 when anything is outdated.

solid answer

~40 s

`bundle outdated` compares the versions Gemfile.lock pins with what the sources offer and prints a table: gem, current version, latest version, the Gemfile requirement, groups and release date. It is read-only, changing neither the Gemfile nor the lockfile, and exits 0 when everything is current and 1 otherwise, which makes it usable as a CI check. By default it reports the newest release even if your requirement forbids it; `--strict` (alias `--filter-strict`) limits it to versions the Gemfile allows, the ones a `bundle update` could actually reach. `--filter-major`, `--filter-minor` and `--filter-patch` filter the output by the size of the jump without changing resolution, and combine. `--only-explicit` hides transitive dependencies, `--group=test` limits it to one group, `--pre` includes prereleases, and `--parseable` prints machine-friendly lines.

code

bash · 6 lines
bash
bundle outdated                          # everything with a newer release
bundle outdated --only-explicit --strict # Gemfile gems, within their requirements
bundle outdated --filter-major           # only major-version jumps
bundle outdated --group=test --parseable # one group, script-friendly

bundle outdated --only-explicit || echo "dependencies drifted"

go deeper

for a junior

Recall that bundle outdated lists gems with newer versions and does not change any file; bundle update is what upgrades.

for a middle

Explain the Current, Latest and Requested columns, why --strict can report a different latest, and how the filter flags only hide rows.

for a senior

Build a dependency-drift routine: only-explicit and strict for routine updates, filter-major for planned upgrades, and the exit status as a CI signal.

for a principal

Decide how the team turns outdated reports into upgrade work, balancing automated drift checks against review capacity and upgrade risk.

## What the command answers `bundle outdated` answers "what could I upgrade?" without upgrading anything. It reads the locked versions from **Gemfile.lock**, asks the gem sources for newer releases and prints what it finds. It never writes to the Gemfile or the lockfile; changing versions is `bundle update`'s job. A run might print (dates shortened to placeholders; `billing-client` is an internal gem): ```bash $ bundle outdated Gem Current Latest Requested Groups Release Date billing-client 2.1.4 3.0.0 ~> 2.1 default YYYY-MM-DD rubocop 1.88.0 1.91.0 >= 0 development, test YYYY-MM-DD ``` The columns: - **Current** - the locked, installed version. - **Latest** - the newest version the source offers (prereleases ignored unless `--pre`). - **Requested** - the Gemfile requirement: `>= 0` for a declared gem without one, empty for a transitive dependency. - **Groups** - the groups the gem is declared in. - **Release Date** - when the latest version was published. ## Exit status The man page is explicit: when every gem is up to date, `bundle outdated` exits **0**; otherwise it exits **1**. A CI job can therefore fail, or open a ticket, when dependencies drift, and a script can branch on the status instead of parsing text. ## Narrowing the output | Option | Effect | |---|---| | `GEM ...` | check only the named gems | | `--strict` / `--filter-strict` | only versions allowed by the Gemfile requirements | | `--filter-major` / `--filter-minor` / `--filter-patch` | only jumps of that size; combinable | | `--only-explicit` | only gems declared in the Gemfile, not their dependencies | | `--group=test` / `--groups` | one group / output organised by group | | `--pre` | consider prerelease versions | | `--local` | use the local gem cache, no network | | `--parseable` / `--porcelain` | minimal, machine-readable lines | Two distinctions trip people up: 1. **Filters versus strictness.** `--filter-major` and its siblings only hide rows; the man page stresses they "do not affect the resolution of versions". `--strict` changes *which* newer version is reported: the newest your requirements permit. With `billing-client ~> 2.1` and a 3.0.0 release upstream, a plain run reports 3.0.0 while `--strict` reports the newest 2.x. 2. **Patch-level flags.** `--patch`, `--minor` and `--major` are the conservative-update preferences shared with `bundle update`; they imply `--filter-strict`. Combined with `--strict` they show what a matching `bundle update --patch --strict` would move to. `--only-explicit` is the most useful filter on a large bundle: transitive gems are usually upgraded by bumping the gem that pulls them in, so hiding them keeps the list to decisions you actually own. ## Cooldown If the project uses Bundler's `cooldown` setting, which makes resolution ignore versions younger than N days, `bundle outdated --cooldown=N` annotates versions still inside the window instead of hiding them. ## Removed spelling Bundler 2's `bundle show --outdated` raises in Bundler 4; `bundle outdated` is the only form. ## Reading the report A few habits make the output actionable: - A row whose **Latest** falls outside **Requested** is blocked by your own Gemfile; upgrading it means editing the requirement, not just running `bundle update`. - A row with an empty **Requested** is a transitive dependency; it normally moves when you update the gem that depends on it. - **Groups** tell you the blast radius: an outdated gem only in `test` cannot break production directly. - `--parseable` prints one line per gem, such as `rubocop (newest 1.91.0, installed 1.88.0, released ...)`, which scripts can split without parsing a table. ## A routine that works - Run `bundle outdated --only-explicit --strict` weekly to see upgrades the Gemfile already allows. - Run plain `bundle outdated --filter-major` to find gems whose requirement blocks a new major release, and plan those upgrades. - Apply the result with `bundle update <gem>`, review the lockfile diff, run the tests.

  • `billing-client` is locked at 2.1.4 with `~> 2.1` in the Gemfile, and 3.0.0 exists on its server. What do plain `bundle outdated` and `bundle outdated --strict` report as latest?
    The plain run reports the newest release, 3.0.0, even though `~> 2.1` forbids it, so the row signals a requirement to revisit. `--strict` reports the newest version the Gemfile allows, the latest 2.x, which is what `bundle update billing-client` could install without editing the Gemfile.

saying these in an interview costs you the question

  • bundle outdated updates Gemfile.lock to the latest versions
  • --filter-major changes which versions the resolver considers
  • bundle outdated exits 0 even when gems are outdated
  • Without --strict it only shows versions the Gemfile allows
  • bundle show --outdated is the Bundler 4 spelling