Score each upgrade on two axes, the risk of staying put and the effort to move, then do high-risk, low-effort items first, schedule high-risk, high-effort items as named projects, and batch the rest. A four-box matrix is enough. Complicated scoring models tend to hide guesses behind decimals.
Define the two axes plainly
Risk of staying put answers: what happens if we do nothing this quarter? Score it 1 to 3.
- 3: end of support inside the quarter, a known advisory in code we call, or an exposed service.
- 2: advisory present but not reachable, or support ends within a year.
- 1: only cosmetic staleness.
Effort to move answers: how much of the team's time does this take? Score it 1 to 3 using your own past upgrades as the reference.
- 1: patch or minor bump with passing tests.
- 2: major bump with a documented migration guide.
- 3: major bump with removed APIs, or an upgrade that touches many services.
Two axes, three levels each. Anything finer gives false precision.
The four boxes
| Low effort | High effort | |
|---|---|---|
| High risk | Do first, this sprint | Named project with owner and date |
| Low risk | Batch into a routine upgrade day | Defer, with a written reason |
Items in the low-risk, high-effort box deserve honesty. Writing "deferred, revisit next quarter, reason: no exposure" is a perfectly good outcome. Leaving it unlabeled is how backlogs grow.
Where the risk score comes from
Do not set the risk score by gut feel alone. Use evidence you can point at:
- Check the project's published support policy for end-of-support dates.
- Match your versions against advisories and note whether a fixed version exists.
- For each advisory, check whether your code reaches the vulnerable function. Reachability analysis does this at scale, and a manual call-graph check works for a single library.
- Ask whether the service is internet-facing.
A known-exploited flag from a public catalog is a reason to raise a score immediately, regardless of effort.
Common mistakes
- Scoring effort optimistically. Use past actuals. Teams routinely under-estimate migrations that touch shared code.
- Letting loud tickets win. A noisy request from a stakeholder should change the score only if the underlying risk changed.
- Ignoring transitive dependencies. The package you never chose can carry the advisory. See our post on testing a transitive bump nobody asked for.
- Scoring once. Re-score at the start of each quarter, because advisories and support dates move.
Try it yourself
Click through it at your own pace, or open it full screen. Watch how filtering to reachable findings shrinks the high-risk column.
Where Safeguard fits
Safeguard's reachability analysis marks which advisories sit in code your application actually calls, which feeds the risk axis directly. You can run a free scan at gold.safeguard.sh/scan.
Quick check
Why only three levels per axis?
Past a few levels, scores mostly reflect mood. Three levels force a clear decision and are easy to explain in a planning meeting.
Who should assign the scores?
The engineers who will do the work score effort. The manager and security partner score risk. Compare and discuss any gap of two levels.
Last reviewed: 19 October 2026.