The libraries that block modernization are the ones you cannot upgrade without changing a lot of code: unmaintained packages, frameworks tied to an old runtime, and anything with no fixed version for a known vulnerability. Find them by combining version age, advisory data and how widely each library is used in your code.
Three kinds of blockers
Unmaintained libraries. The project has no recent releases and no one answering issues. If a vulnerability appears, there may never be a fix. You must replace it, wrap it or remove the feature.
Runtime locked libraries. Some packages only work on an old language runtime. Upgrading the runtime to receive security fixes forces an upgrade of those libraries, and the other way around. These pairs are the classic reason teams stay on old versions for years.
Deep framework coupling. A web framework or ORM woven through thousands of files cannot be swapped in one pull request. The strangler fig approach exists for this reason. Microsoft's guidance notes that as systems age, the tools, hosting technology and architectures they rely on can become obsolete.
How to find them
- Export the full resolved dependency list for each artifact.
- For each library, record the installed version, the latest version, and the date of the latest release. Registries expose this.
- Check each library against vulnerability data and note whether a fixed version exists.
- Count how many source files import it. High count plus no fix is a blocker.
- Check the runtime or platform each library requires.
A simple scoring table helps teams agree on order.
| Signal | Points toward blocker |
|---|---|
| No release in a long time | Higher |
| Known vulnerability, no fix | Highest |
| Imported in many modules | Higher |
| Requires old runtime | Higher |
| Has an actively maintained alternative | Lower, replacement is clear |
You choose the thresholds. The goal is a ranked list, not a precise number.
What to do with each blocker
- Replace when a maintained alternative exists and the surface area is small.
- Wrap the library behind an interface, so new services call your interface and the old library lives in one place. This is the anti-corruption layer idea applied to a dependency.
- Isolate the component in a separate process with restricted network access when you cannot replace it yet.
- Accept with an expiry when risk is low and the replacement is scheduled. Record the owner and date.
Where reachability helps
A blocker with a known vulnerability is not equally urgent in every system. If the vulnerable function is never called in your code and the component is not internet facing, the risk is lower than the raw advisory suggests. If it is reachable and public, it moves up the plan. See legacy software supply chain risks for the wider picture.
Try it yourself
Click through it at your own pace, or open it full screen.
Notice how findings can be grouped and prioritized rather than worked in alphabetical order.
Safeguard's dependency scanning and reachability analysis can produce the inputs for this triage, and its free scan at gold.safeguard.sh/scan shows a first inventory.
Quick check
Is an old library always a blocker?
No. Old but stable libraries with no known vulnerabilities and a small footprint can wait.
What if there is no replacement?
Wrap it, isolate it, and write down the exception with an owner and a review date.
Sources
Last reviewed: 19 October 2026.