skip to content

In Composer, why can composer update vendor/package fail, and how do -w, -W and --minimal-changes change which packages it may touch?

level: middleimportance: should knowfreq 40%

answer

  1. partial update keeps the rest locked
  2. -w: its dependencies, not root requirements
  3. -W: root requirements too
  4. -m: only what must change
  5. bump raises lower bounds afterwards

basics

~20 s

composer update vendor/package frees only that package; its dependencies stay locked, which can block a newer release. -w also frees its non-root dependencies, -W root ones too, and --minimal-changes (2.7+) moves transitive packages only where required.

solid answer

~50 s

A **partial update** puts only the named packages on the allow-list; every other package stays at its locked version. If the new release of `vendor/package` needs a newer version of one of its own dependencies, the solver cannot move that dependency, so it either keeps the old version or reports that the requirements could not be resolved. `-w` (`--with-dependencies`) adds the named package's dependencies to the allow-list, **except** those that are also root requirements in your `composer.json`; `-W` (`--with-all-dependencies`) includes those as well. Both can move many packages at once, so `-m` (`--minimal-changes`, Composer 2.7+) keeps transitive packages at their locked versions unless they must change. `--patch-only` (2.8+) limits updates to patch releases. After an update, `composer bump` raises the lower bounds in `composer.json` to the installed versions; do not run it on a library's `require`.

code

bash · 4 lines
bash
composer update acme/api-client            # fails: acme/http stays locked
composer update acme/api-client -w -m       # frees acme/http, minimal diff
composer update acme/api-client -W          # also frees root requirements
composer bump --dry-run                     # preview raised lower bounds

go deeper

for a junior

Recall that you can name packages after composer update to update only those.

for a middle

Explain the allow-list, why a partial update fails when a dependency must move, and the difference between -w and -W.

for a senior

Update one package with -w or -W plus -m, read the lock diff before committing, and use bump deliberately on applications only.

for a principal

Choose the team's update cadence and scope rules, balancing small reviewable lock diffs against falling behind on transitive security fixes.

## The allow-list behind a partial update `composer update` with no arguments may change every package. With arguments, it becomes a **partial update**: the named packages (wildcards like `"vendor/*"` work) are placed on an **allow-list**, and every other package is **fixed** at the version recorded in `composer.lock`. The solver then looks for the newest versions of the allowed packages that are compatible with everything fixed. That is what makes partial updates safe, and also what makes them fail. ## Why `composer update vendor/package` fails or does nothing Suppose `acme/api-client` 3.2 is locked and 3.4 is out. Version 3.4 requires `acme/http ^2.1`, but the lock has `acme/http` 2.0. With `composer update acme/api-client`: - `acme/http` is fixed at 2.0, because it is not on the allow-list; - 3.4 needs 2.1+, so it is not installable; - the solver keeps 3.2 or reports that your requirements could not be resolved, depending on constraints. The fix is to let the dependency move too. ## `-w`, `-W` and `-m` | Flag | Also allowed to change | Typical use | |---|---|---| | none | only the named packages | a simple bump within compatible dependencies | | `-w`, `--with-dependencies` | the named packages' dependencies, **except** ones that are root requirements | the example above, when `acme/http` is only transitive | | `-W`, `--with-all-dependencies` | the dependencies, **including** root requirements | when you also require `acme/http` directly in `composer.json` | | `-m`, `--minimal-changes` | as `-w`/`-W`, but only packages that cannot stay locked | keep the diff small | `-w` stops at root requirements because those are packages **you** declared; Composer assumes you want to update them explicitly. If `acme/http` appears in your own `require`, only `-W` (or naming it as an argument) lets it move. The catch is breadth: `-W` on a well-connected package can move dozens of transitive packages in one commit. **`--minimal-changes`** (`-m`), added in Composer 2.7, tells the solver to keep every transitive package at its locked version if possible and change only those that must change for the allow-listed packages to update. The allow-listed packages themselves are still updated fully. Composer 2.9 extended `-m` to full updates, where only packages that must change to satisfy edited constraints move, and added the `update-with-minimal-changes` config setting. The same `-w`, `-W` and `-m` flags work on `composer require` and `composer remove`, which run a partial update of the package they touch. ## Other ways to narrow an update 1. `--patch-only` (Composer 2.8+): only patch-level updates for installed packages, a lower-risk way to refresh everything. 2. `--root-reqs`: restrict the update to first-degree dependencies. 3. `composer update vendor/package:2.0.1`: a temporary inline constraint for that run, which must be a subset of the `composer.json` constraint and does not edit the file. ## `bump`: raising the floor after an update `composer bump` rewrites the constraints in `composer.json` so their **lower bound** equals the currently locked version: `^1.0` with 1.2.1 locked becomes `^1.2.1`; `^1.2 || ^2.3` with 2.4.0 locked becomes `^1.2 || ^2.4`. It refuses to run when the lock is stale, and it refreshes the lock's content-hash afterwards. The benefits are that a later conflict cannot silently **downgrade** a package, and resolution has fewer versions to consider. `update --bump-after-update` runs it automatically. Composer's documentation is explicit that running `bump` blindly on a **library** is not recommended: raising a library's `require` floors narrows what consumers can install. `bump --dev-only` is fine for libraries, because dev requirements never reach consumers. ## Reading what the update did After the solver runs, Composer prints `Lock file operations: N installs, N updates, N removals` followed by one line per changed package with its old and new version. That line is the first thing to read after a `-W`: if you asked for one package and see fifteen updates, decide whether that is acceptable before you run the tests, or retry with `-m`. The `git diff` of `composer.lock` shows the same changes with the exact references, and it is what a reviewer sees in the pull request. ## A safe single-package routine 1. `composer update acme/api-client -w -m` 2. Read the lock diff: which packages moved and by how much. 3. Run the tests; commit `composer.json` and `composer.lock` together.

  • Why doesn't -w let a package move if it is also in your root require?
    Root requirements are packages you declared yourself, so `-w` treats them as yours to update explicitly and leaves them locked. Use `-W`, or add the package to the arguments of `composer update`, when that dependency must move too.
  • Why is composer bump risky on a library but fine on an application?
    In an application, raising the lower bounds only records what is already locked. In a library, `require` constraints are imposed on every consumer; raising them to the versions you happen to have locked narrows what consumers can install and can create conflicts. `bump --dev-only` is safe because dev requirements never reach consumers.

saying these in an interview costs you the question

  • composer update vendor/package also updates all of that package's dependencies
  • -w and -W are the same flag with different spellings
  • --minimal-changes stops the named package itself from being fully updated
  • composer bump upgrades packages to newer releases
  • Running composer bump on a library's require section is always safe