Published · Europe/Paris
Declared vs observed: when composer.json lies
A composer.json constraint is a claim, not a fact. Somebody wrote "shopware/core": "^6.5"
at a point in time, with whatever testing they'd actually done at that point, and the file has
said the same thing ever since — regardless of whether the code underneath it kept up. Two
ways that claim goes stale, and both matter for anyone planning an update window from it.
Declared-compatible, actually broken
The more agencies worry about is the false positive: a constraint that technically admits your
target version, but the plugin was never run against it. This is the caret trap in its purest
form — ^6.5 mathematically includes 6.7.0.0 whether or not the plugin author has ever seen
6.7. The constraint isn't lying exactly; it's just answering a narrower question than the one
you're actually asking. "Is 6.7 inside the range I declared" and "does this plugin work on 6.7"
are different questions, and only the second one is the one that matters on update day.
Declared-incompatible, actually fine
The less-discussed direction is the false negative: a constraint that excludes your target even
though the plugin, in practice, would run fine on it. Plugin authors are often conservative —
they cap a constraint at the newest version they'd tested at release time, then never bother
loosening it once the plugin turns out to keep working on later minors. Upgrade Radar will correctly flag
that as CONFLICT, because that's what the declaration says, even in cases where an agency that's
actually run the update knows better. This is precisely why Upgrade Radar's own report never claims to
be the final word: it checks constraint declarations, not a full dependency resolution, and
composer update --dry-run against the real tree stays the definitive gate at update time.
Every report carries that scope note explicitly — "checks declared constraints only", "run
composer update --dry-run at update time", "transitive non-shopware dependency conflicts out of
scope in v0" — printed, not buried in documentation nobody reads.
The honest middle: UNKNOWN
There's a third state worth naming, because it's the one most tools quietly collapse into
"probably fine": a plugin with no shopware/* constraint declared at all. No claim, in either
direction. Upgrade Radar marks that UNKNOWN and stops there — it never assumes an undeclared plugin is
safe just because nothing said otherwise. An agency reading UNKNOWN knows exactly what it means:
go check this one by hand, we genuinely can't tell you from the declaration alone.
Three verdicts, not two, is a small design choice with a real consequence: it keeps the tool from ever quietly rounding an "I don't know" up to a "yes." A checker that only ever prints COMPATIBLE or CONFLICT has, somewhere, decided what to do with the cases it can't actually resolve — and that decision is usually invisible to whoever's reading the report.
What actually closes the gap
Declared-vs-observed isn't a problem a smarter constraint parser solves, because the parser is reading exactly what's there — correctly. What closes the gap is data: real outcomes from real updates, tracked against what the declaration said beforehand. That's the piece Upgrade Radar is building toward next. The schema for it already exists — an append-only log of individual observations, each one pairing a plugin's declared verdict against a target version with what actually happened when an agency ran the update for real (worked, broke, partial, or still unknown) — designed before the first pilot user, specifically so early feedback has somewhere structured to land instead of getting lost in an inbox.
To be precise about where that stands today: the schema is designed, nothing is populated yet. No knowledge base of observed-vs-declared outcomes exists in the product right now — that's the honest state of it, and it's exactly the kind of thing this note would be the wrong place to overclaim. What exists today is the constraint checker itself, and its habit of telling you plainly when it's reading a declaration rather than a verified fact.
Where this is going
Every design partner who runs Upgrade Radar against a real update and reports back what actually happened — caught something real, missed something, or confirmed a CONFLICT was actually fine — feeds that gap-closing data. None of that requires trusting Upgrade Radar blindly in the meantime: the constraint checker tells you exactly which constraint excluded a plugin and why, so you can always verify the declaration yourself before you act on it.
Check your own declarations
If you want to see where your own shops' declared and (as far as you know) actual compatibility might already be diverging, the fastest way is to run a check against a shop you know well and compare the verdicts to what you already know happened.