"It's a minor bump, should be fine" is the sentence right before most dependency-upgrade incidents. The changelog and the semver number both describe what the maintainer intended to ship, not what your specific code path actually exercises, and not what moved underneath in the packages you didn't directly ask to upgrade. Real risk lives in three places that neither number covers.
A breaking change only breaks you if you call the thing that broke. A changelog entry reading "removed deprecated Client.request() callback signature" is irrelevant if your code only ever used the promise-based API — and is a guaranteed outage if even one call site, possibly in a rarely-touched internal script, still uses the old form. Maintainers document changes against their own API surface; they have no way to know your usage surface.
Check: grep your codebase for every symbol the changelog names as changed or removed before you upgrade, not after the build breaks. For a dynamically-typed language this means a text search across the whole repo, including config files and scripts, not just the primary source directories — the call site that breaks is disproportionately likely to be the one nobody remembered was still there.
Upgrading one direct dependency can move a dozen transitive ones, and semver ranges mean a routine npm install or pip install -U without pinning can shift packages you never intentionally touched. The direct dependency's changelog says nothing about them, because from the maintainer's point of view, their own dependencies are an implementation detail — until one of them changes behavior you were unknowingly relying on.
Check: diff the full lockfile before and after, not just the one package you meant to bump. Anything that moved a major version transitively deserves the same scrutiny as a direct upgrade — read its changelog too, even though nothing in your own command line named it.
Semver encodes what a maintainer believes is a breaking change, and maintainers disagree with each other and with you about where that line is. A patch release has shipped a de facto behavior change (a bug fix that some callers were unknowingly depending on), and a major release has shipped with less real-world impact than a "minor" one that changed a default. The version number is a signal, not a guarantee.
Check: for anything load-bearing, read the actual diff of the parts you use — not just the changelog prose — regardless of whether the bump is patch, minor, or major. Trust the version number for triage priority (major gets more scrutiny than patch), never as a substitute for checking.
Usage surface comes first because it's the fastest filter — most changelog entries are irrelevant to you the moment you confirm you don't call the affected symbol, which eliminates most of the reading work immediately. Transitive movement comes second because it's the one people forget to check at all, since nothing in the direct upgrade command points at it. Semver trust comes last because it's a prioritization heuristic, applied after you already know which changes are real — not a substitute for the first two checks.
None of this requires an LLM — it's a grep, a lockfile diff, and reading the parts of a diff that actually matter. Where a model helps is doing all three fast across a large diff: cross-referencing a changelog's named symbols against your actual call sites, flagging which transitive packages moved a major version, and summarizing only the diff hunks that touch code you use — which is what the dependency-upgrade-planner skill in the Claude Code Power Pack does. If the upgrade already caused an incident, see the companion guide on blameless postmortems.