Produce a sorted list of name and version pairs from the old lockfile and the new one, then diff the two lists. Every line that differs is a version change caused by the migration. Explain each one or fix it before merging. Do the check on the installed tree and on the lockfile, because they can disagree.
Why versions drift during a migration
A package manager migration looks like a tooling change, but a resolver is the part that picks versions. Different tools resolve conflicts differently. The Maven documentation says Maven picks the nearest definition in the tree, and the Gradle documentation says Gradle by default selects the highest version. A move between those two can change what ships even when no one edited a version number. Within one ecosystem the same thing happens when a lockfile is regenerated instead of converted: the uv documentation notes that without locking, versions can change over time, when using a different tool, or across platforms.
So the question is never whether versions can drift. It is whether you checked.
A method that works for any ecosystem
- Before touching anything, export the old tree. Use the tool's own listing command and write name and version only, one pair per line.
- Make the migration change on a branch.
- Export the new tree the same way.
- Sort both files and run a plain diff.
- Sort the differences into three groups: expected (the tool added or dropped a platform specific package), upgraded, and downgraded.
- Investigate every downgrade first. A downgrade can reintroduce a version with a known advisory.
- Repeat the diff on the built artifact if you can, such as the container image or the packaged archive.
# example for a Node project, names and versions only
npm ls --all --parseable > before.txt
# after migrating, produce the same style of list with the new tool
# then compare
diff <(sort before.txt) <(sort after.txt)
Flags and output formats differ between tool versions, so check the help output of your versions before scripting this.
Compare the installed tree, not only the lockfile
A lockfile is a plan. The installed environment is what runs. The uv documentation says an install into an existing environment does not remove packages that are not in the lockfile, which means a developer machine can carry extra packages that CI never sees. Run the comparison in a clean checkout or a fresh container so that leftovers do not hide a difference.
Record the result
Paste the diff summary into the pull request description. Reviewers see a short list of reasons instead of a thousand line lockfile. The same record helps an auditor later, because it shows that a tooling change was controlled. See also what to check in a lockfile diff during code review.
Try it yourself
Click through it at your own pace, or open it full screen. Notice how each component is listed with an exact version, which is the shape of data you need on both sides of the diff.
Safeguard's dependency scanning can generate an SBOM for the branch before and after, and the free scan at gold.safeguard.sh/scan gives you a quick first look.
Quick check
Is a lockfile diff enough?
It is a good start, but check the installed tree or built artifact too, because leftovers and platform differences only show up there.
Which differences matter most?
Downgrades. A lower version can bring back a vulnerability that the old tree had already fixed.
Sources
- Maven dependency mechanism, dependency mediation
- Gradle graph resolution, version conflicts
- uv, locking environments
Last reviewed: 19 October 2026.