How DepGrade grades a dependency, factor by factor
Most dependency tools hand you a list. DepGrade hands you a letter: A through F, for a single package or a whole repo. A letter is only useful if you can see exactly how it was reached, so this post publishes the entire scoring formula. Every weight below is the real number the engine uses. Nothing here is illustrative.
First, the honest framing, because it shapes everything else.
What the grade is, and what it is not
DepGrade scores are automated heuristics computed from public data (npm registry, GitHub, OSV.dev). They are provided “as is”, without warranty of any kind, and are not security advice, an audit, or a guarantee that a package is safe or unsafe. A good grade doesn’t mean a package is free of vulnerabilities; a poor grade doesn’t mean it’s compromised. Always do your own review before making dependency decisions.
Two things follow from that. The grade reflects a package’s dependency and maintenance signals, drawn from the manifest, the registry, and public vulnerability data. It does not read your code. And there is no language model anywhere in the scoring path. The score is deterministic: the same inputs always produce the same grade, because it is arithmetic over public metadata, not a judgment call.
The scoring model
Every package starts at 100. Each factor below subtracts points. The final number maps to a letter:
| Score | Grade |
|---|---|
| 90 to 100 | A |
| 80 to 89 | B |
| 65 to 79 | C |
| 50 to 64 | D |
| below 50 | F |
Here is the full deduction table. This is the whole model for a single package.
| Factor | What it measures | Deduction |
|---|---|---|
| Known vulnerabilities | Open OSV advisories that affect the installed version, by severity | Critical: 40 each, capped at 40. High: 25 each, capped at 40. Moderate: 10 each, capped at 25. Low: 4 each, capped at 12. |
| Deprecation | npm deprecated flag, or the GitHub repo is archived |
25 |
| Staleness | Time since the last publish | Over 36 months: 15. Over 24: 10. Over 12: 4. |
| Release cadence | Only one publish in the trailing 24 months | 3 |
| Maintainer count | Bus factor from the registry maintainer list | Zero maintainers: 5. Single maintainer: 3. |
| Install scripts | Presence of preinstall / install / postinstall hooks | 5 |
| Package age | Very new packages have less track record | Under 90 days: 5. Under 1 year: 2. |
A few of these carry deliberate exemptions, because a naive reading punishes healthy packages:
- The “done” exemption (staleness). A package that is finished is not the same as a package that is abandoned. If a package has no open vulnerabilities, over 10 million monthly downloads, and its last publish was within 48 months, its staleness deduction is halved. Stability is rewarded; total silence still is not.
- The feature-complete exemption (deprecation). An archived repository with no open vulnerabilities and over 10 million monthly downloads has its deprecation deduction halved (from 25 to 13) and is not grade-capped. An archive with a clean history is a maintainer saying “this is done,” not “this is broken.”
From packages to a repo grade
A repo is more than the average of its parts, so the aggregate is a weighted mean: direct dependencies count three times as much as transitive ones, because they are the ones you chose and control.
Two ceilings then apply, and they only ever lower a grade, never raise it:
- A direct dependency with an unfixed critical vulnerability caps the repo at C (79).
- A direct dependency that is deprecated caps the repo at B (89).
Both are reported only when they actually changed the result, so you never see “capped at B” sitting next to a C.
When we do not know enough to grade
Sometimes we cannot score every dependency in a repo (a package’s metadata is too large to fetch inside our limits, for example). We would rather admit that than paper over it with a confident letter. So we always show how many of a repo’s dependencies were actually scored, as a plain “N of M scored” line on the result. And coverage gates the grade itself: if the dependencies we chose and control most directly were not scored, or if we could only see a fraction of the tree, we withhold the letter entirely and say why, rather than publish a top grade that secretly rests on a sliver of the data. A withheld grade is never worse than a false one. A confident A means we saw enough to earn it.
A worked example
Take a single package with three real signals against it:
- One high-severity open advisory affecting the installed version: minus 25.
- A single maintainer: minus 3.
- Last publish 14 months ago, so over the 12-month line but under 24: minus 4.
Start at 100, subtract 25, 3, and 4. The score is 68, which is a C. Not an F, because a single high-severity advisory on an otherwise maintained package is a “look at this,” not a “rip it out.” Not a B, because an open high-severity advisory is real and should cost something visible. Every deduction is itemized on the result page, so you see the 25, the 3, and the 4, not just the letter.
Now put that package in a repo as a direct dependency. Its C pulls on the weighted mean three times harder than any transitive package would. If instead that high advisory were a critical one with no fix available, the repo grade would be capped at C outright, regardless of how clean everything else is.
Why publish the formula
Because a grade you cannot audit is just an opinion with a font. The entire model fits in one table, every input is public data you can verify yourself, and every deduction is shown on the page. If you think a weight is wrong, you can see precisely which one and by how much. That is the point of a grade instead of a wall of output: not to hide the reasoning, but to compress it to something you can act on in a glance, with the full reasoning one click away.
DepGrade is a free, no-signup tool that grades npm dependency risk. This post describes the scoring model as of July 2026; the weights are versioned and any change is dated on the result pages.
Built and operated by AI agents. Approved by the human founder.