The composer.json diff for the Drupal 10 to 11 upgrade on this site is thirteen lines: a handful of version constraints bumped, doctrine/annotations added back, symfony/runtime allow-listed. The composer.lock diff for that same commit is 2,109 lines changed — 1,416 insertions, 693 deletions, one file. That gap between what I asked for and what the lock file recorded is the actual cost of -W, and it's worth looking at directly instead of trusting the flag's name to describe it.
What changed about how composer was allowed to resolve
The first attempt here was a plain composer require drupal/core-recommended:^11, and it failed immediately — the solver returned page after page of "found but not loaded, likely because it conflicts with another require" for every 11.x release in turn, composer's way of saying something is blocking the resolve without saying what. A narrower resolve only tries to satisfy the new constraint by moving the smallest number of packages it can get away with, leaving everything else pinned exactly where the lock file already had it. --with-all-dependencies removes that restriction: every package in the tree becomes eligible to move to whatever version the solver now prefers, not just the ones on the direct path to the new constraint. With -W, the same command succeeded. It also touched far more of the lock file than the thirteen-line composer.json diff would suggest.
What actually moved
Some of the 2,109 lines are exactly what you'd expect from a major core upgrade: drupal/core-recommended 10.4.6 to 11.4.5, symfony/console v6.4.43 to v7.4.16 to satisfy core's own new constraint, consolidation/robo 4.0.6 to 5.1.1 because the old major pinned symfony/console to ^6. Those are direct consequences, traceable in one hop from the thing being upgraded.
Others aren't:
sebastian/diff, a PHPUnit component with nothing Drupal-specific about it, moved from 4.0.6 to 7.0.0 — three major versions in one resolve, with nothing in composer.json referencing it directly.consolidation/self-updatedisappeared from the lock file entirely, dropped as a transitive dependency of the newerrobo, even though nothing about the core upgrade asked for that package by name.- Four packages that weren't in the project before this commit showed up anyway:
justinrainbow/json-schema,marc-mabe/php-enum,php-tuf/composer-stager, andrevolt/event-loop— dependencies of dependencies of the packages that did change. doctrine/lexermoved from 2.1.1 to 3.0.1, not because anything asked for a lexer upgrade, but because re-addingdoctrine/annotationsafter this same resolve had dropped it required a newer lexer underneath.
None of this broke anything. Composer's solver is doing exactly its job: finding one mutually satisfiable set of versions across the whole tree, and every one of those moves is a legitimate answer to that problem. But "legitimate" and "expected from reading composer.json" turn out to be different claims, and the gap between them is roughly the size of a 2,109-line diff against thirteen lines of stated intent.
Why the size of the diff is the actual risk
No specific package broke here, and the full local publish-pipeline test after the upgrade passed clean. The practical cost is that reviewing a -W resolve line by line stops being realistic well before you'd finish, which means the honest review surface shrinks down to "did the tests pass" and "does the site still respond correctly" rather than "do I understand every change I just accepted." For a major-version upgrade where the narrower resolve had already failed outright, that's a reasonable trade to make. It's still a trade, not a free upgrade of scope, and it's worth knowing which one you're making before you type -W out of habit.
What's worth checking next time
The fix isn't avoiding -W — here, the narrower resolve had already failed on its own, so the flag wasn't optional. It's treating the resulting lock diff as something worth skimming for scope once, rather than only checking that the tests passed: a quick pass over which packages jumped more than a minor version, and which ones showed up that weren't there the day before, catches the sebastian/diff-shaped surprises while they're still fresh in memory, instead of six months from now when something in one of those dependencies' behavior actually matters and nobody remembers it changed. The flag does exactly what its name says. Seeing the actual size of what that produces, at least once, is the part worth doing yourself rather than trusting the name to bound it.