A green build proves that the versions in your lockfile compile and pass tests. It says nothing about whether they are current, supported or free of known flaws. Lockfile drift is the gap between the pinned versions and what the registry offers today, and it grows silently because nothing fails.
What a lockfile does
A lockfile records the exact version of every direct and transitive dependency so that installs are reproducible. That is a good property. The side effect is that nothing changes until someone changes it, so a repository can run the same versions for years while the registry moves on.
Why drift stays invisible
- Tests pass. They test behavior, not freshness.
- Ranges hide it. In npm, the wanted column is the highest version your declared range allows, and latest is the registry tag. If the range is narrow, an install never moves past it, and you see no change.
- Transitive packages are out of sight. Most pinned versions belong to packages you never chose.
- Nobody owns it. No one is paid to notice a quiet lockfile.
How to surface drift
- Run your package manager's outdated command on a schedule, and keep the output. In npm, outdated lists current, wanted and latest for each package.
- Check the lockfile's age. The last commit date on the lockfile is a crude but telling signal. A lockfile untouched for a year deserves a look.
- Diff against a fresh resolution. Resolve dependencies into a temporary directory and compare with the committed lockfile.
- Scan the lockfile for known vulnerabilities, not just the manifest.
- Add a CI job that fails or warns when drift passes a threshold you choose.
What to do with the output
| Finding | Interpretation | Action |
|---|---|---|
| Wanted equals current, latest far ahead | Range or major blocking | Plan a deliberate upgrade |
| Current behind wanted | Install not refreshed | Run an update in a branch |
| Old lockfile, many transitive gaps | Silent aging | Regenerate in a controlled PR |
| Known vulnerable transitive | Needs override or parent update | Prioritize by reachability |
Keep reproducibility
Do not give up pinning. The aim is to update on purpose, in small reviewed pull requests, while keeping builds reproducible. Commit the lockfile, review its diff, and let CI run tests before merging.
A caution on regeneration
Deleting the lockfile and reinstalling can pull in many changes at once, which brings back the large upgrade problem. Update in slices, a few packages at a time, so a failure points at a small set of changes.
Report it
Add one line to your monthly review: oldest lockfile age by repository and the biggest gap per repo. It gets drift on someone's radar.
Safeguard's SBOM and dependency scanning lists direct and transitive packages with their versions, and reachability analysis shows which outdated or vulnerable ones your code actually calls. Autonomous remediation can open fix pull requests; it is off by default and every category starts on Manual. You can try a free scan at gold.safeguard.sh/scan.
Try it yourself
Click through it at your own pace, or open it full screen. Check that the inventory lists transitive packages with their exact versions.
Quick check
Should CI fail on outdated dependencies?
A warning is usually better at first. Failing builds on freshness alone can block unrelated work. Fail on known exploited or reachable vulnerabilities.
Do lockfiles cause the problem?
No. They make versions visible and reproducible. The problem is leaving them untouched.
Sources
- npm outdated, npm Docs
- Semantic Versioning 2.0.0
- OWASP Top 10:2021, A06 Vulnerable and Outdated Components
Last reviewed: 19 October 2026.