Shadow dependency rate is the number of distinct packages in use that are not on your approved list, divided by the number of distinct packages in use. Count it per repo and for the whole org, record it weekly, and watch the trend. A falling line means your approval process is working.
Define the terms before you count
A metric nobody agrees on is worse than no metric. Fix these definitions in writing:
- Package in use: any package that appears in a lockfile on a default branch, direct or transitive.
- Approved: on the list, at an accepted version range, or covered by an active exception.
- Distinct: one package name counts once per scope you are measuring, whatever the version.
The scope matters. A rate per repo tells you where to talk to people. A rate for the org tells leadership if the program is working.
The formulas
| Measure | Formula | What it tells you |
|---|---|---|
| Shadow rate (org) | Distinct unapproved packages / distinct packages | Overall control |
| Shadow rate (repo) | Unapproved packages in repo / packages in repo | Where to focus |
| Direct shadow rate | Unapproved direct packages / direct packages | Human decisions that skipped review |
| Exception share | Packages covered by exceptions / packages in use | Whether exceptions are carrying the program |
| Unowned share | Unapproved packages in repos with no owner / unapproved packages | Whether findings have anyone to act on them |
Report direct and transitive separately. Transitive packages are rarely a person's decision, so a high transitive rate points at the approval model, not at a team.
Pitfalls
- A stale approved list inflates the rate. If half the packages are flagged because the list is old, you are measuring list quality. See keep an approved package list from going stale.
- Counting versions as separate packages makes the number swing for no reason.
- Including archived repos hides the picture. Decide up front whether they count.
- Celebrating a rate that fell because the approved list got longer without review. Pair the rate with the review date of each list entry.
Set a target honestly
Do not start with a target such as 5 percent. Run the measurement for a month to get a baseline, then pick a direction: for example, no new unapproved direct packages without an exception, and a falling trend on the existing ones. The baseline you measure is the number you work from, whatever it turns out to be.
What to report
A one page monthly view works:
- The org rate and last month's rate.
- The five repos with the most unapproved packages.
- New exceptions and expired exceptions.
- Packages that appeared in ten or more repos without approval, which signal a policy decision to make.
That last item turns the metric into action. A package used everywhere and approved nowhere is a decision waiting to be made.
Try it yourself
Open the continuous monitoring demo and look at how a changing inventory is tracked over time. Think about which line you would add for your own rate.
Click through it at your own pace, or open it full screen.
Quick check
Should dev dependencies be counted?
Yes, but report them separately. They run on developer machines and in CI, so they matter, but their review depth can differ.
What is a healthy rate?
There is no universal number, and we have not found a trustworthy public benchmark. Compare yourself with your own baseline.
Safeguard offers SBOM and dependency scanning, policies and gates, and a free scan at gold.safeguard.sh/scan. See also how to find unapproved dependencies across all repos.
Last reviewed: 19 October 2026.