A developer needs to resize an image. They search, find a package that does it, install it, and the task is done in four minutes. At no point in those four minutes was security the question being answered — and that is not a failure of discipline. It is what the job looks like.
Almost every discussion of dependency risk starts from the wrong premise: that somewhere there is a careless developer who should have checked. Fix the carelessness and you fix the problem.
That framing is comfortable, it is common, and it has not produced a measurable improvement anywhere it has been tried at scale. It is worth understanding why, because the alternative is genuinely better.
What actually happens at the moment a package is added
The sequence is always roughly this. There is a task — parse a date, validate an address, generate a PDF, talk to an API. The developer is somewhere in the middle of a larger piece of work and has the whole shape of it in their head. They need this one piece solved so they can carry on.
They search. Something comes up: a package with a reasonable name, a README with an example that matches their case, enough downloads to look established. They install it, paste the example, adapt it, and it works. Total elapsed time: minutes. Attention spent on the dependency itself: almost none, because the dependency was never the point. The task was the point.
And here is the part that gets missed — that was the correct decision. Writing your own date parsing is how you get bugs. The instinct to reach for a maintained library is the right instinct, and it is the one that makes modern software economically possible at all. We are not looking at a mistake to be corrected.
Why "just review your dependencies" does not work
The advice is not wrong so much as impossible. Three reasons, and each is sufficient on its own:
The information is not visible at the moment of decision. To evaluate a package properly you would want to know: is it still maintained, when was the last real release, how many people can publish to it, what does it pull in, does it run anything at install time, what licence, has it had advisories. None of that is on the page the developer is looking at. Assembling it takes fifteen or twenty minutes — for a decision that took four, on a task that is one of nine that day.
The number defeats you. Even a developer with unlimited discipline reviews the package they chose. They do not review the several hundred that package pulled in, because nobody chose those and most of them are invisible without deliberately going to look. Reviewing forty direct dependencies is a hard afternoon. Reviewing six hundred transitive ones is not a task, it is a job.
It does not stay reviewed. This is the one people forget. A package that was well maintained when you added it can be abandoned two years later. A maintainer's account can be taken over. A flaw can be published in code nobody has touched since. The review you did in March was accurate in March and tells you nothing about today. A one-off check answers a question that expires.
The asymmetry that makes this worth solving
Adding a dependency takes about thirty seconds of thought. Removing one takes weeks, if it can be done at all — by then it has been imported in forty files and something else depends on it too. The decision is cheap and reversible in principle, expensive and permanent in practice.
That mismatch is not fixable by asking people to think harder for those thirty seconds. It is only fixable by making the consequences visible later, cheaply, without anyone having to remember.
AI assistants do the same thing, faster
If a human developer reaches for a package without evaluating it, an AI assistant does the same thing with less context and more confidence. It suggests a library that fits the problem, with an example that works. It does not know whether the package is maintained. It does not know whether it was published last week. It sometimes suggests packages that do not exist at all, which attackers have learned to register.
The person reviewing that suggestion is frequently not equipped to spot the difference — and increasingly, the code works well enough that nobody looks hard. The volume of dependencies entering projects has gone up, the deliberation per dependency has gone down, and both trends point the same way.
And if the assistants are arriving because someone above asked for them, there is a prior question worth settling: what the agent is allowed to reach, and what data leaves with it.
What to do instead
The principle is simple: stop asking for attention at the moment it is least available, and put the information where the work already is.
Make the check automatic and continuous. Not a portal someone has to remember to open. If knowing requires a deliberate act, it will happen twice and then stop. The check has to run whether or not anyone thinks about it, and it has to run again next month, because the answer changes without the code changing.
Put it in the pull request. This is the highest-leverage single change, because it is the one moment when a developer is already stopped and already thinking about this specific code. A finding in a pull request is context. The same finding in a weekly email is an interruption, and gets treated like one.
Say what to do, not what is wrong. "CVE-2024-XXXX, CVSS 9.1" asks the developer to go and research something. "Upgrade express to 4.19.2 — that also clears the other three" is an instruction. If your tooling produces the former, someone has to translate it every time, and eventually nobody does.
Give them the two or three that matter. A list of thirty-one findings gets closed. The value is in the ranking: which of these is actually being exploited, and which can wait for the normal upgrade cycle. An unranked list transfers the hard part back to the person who has least time for it.
Explain why they even have it. A finding about a package the developer has never heard of reads as noise until it says express › body-parser › qs. Then it reads as one upgrade.
The point is not to make developers care more
They already care. What they do not have is the fifteen to twenty minutes per package, the visibility into six hundred transitive dependencies, or a reason to re-check something they already checked last year.
Every one of those is a tooling problem wearing a discipline costume. Treat it as a discipline problem and you get a policy nobody follows and a quarterly reminder everyone ignores. Treat it as a tooling problem and the developer keeps doing exactly what they were doing — solving the task in four minutes — while the checking happens somewhere that does not cost them anything.
Why do developers not check dependencies for vulnerabilities?
Developers do not check because at the moment a package is added, security is not the question being answered — finishing a task is. The developer needs one piece solved to carry on with a larger piece of work, they find a package that does it, and it works in minutes. Evaluating that package properly would require information not visible on the page they are looking at, and would take four or five times as long as the task itself. It is not carelessness; the instinct to use a maintained library rather than write it yourself is correct.
Is it enough to review dependencies before adding them?
No — reviewing dependencies before you add them is not enough, for three reasons. The information you would need is not available at the moment of decision without fifteen to twenty minutes of research per package. You can only review the packages you chose, not the several hundred those pulled in, which is where most vulnerable packages turn up. And a review expires: a package that was well maintained when you added it can be abandoned, taken over, or have a flaw published against it years later without anyone touching the code.
How do you get developers to take dependency security seriously?
Mostly by not asking them to care more. The problem is not motivation but the cost of the information: fifteen to twenty minutes per package, no visibility into transitive dependencies, and no reason to re-check something already checked. Put the check in the pull request where the developer is already stopped and already thinking about that code, make it say what to do rather than what is wrong, rank it so only two or three things need attention, and run it continuously so nobody has to remember.
Does using an AI coding assistant make this worse?
An AI coding assistant changes both the volume of dependencies and the deliberation per dependency, in the same direction. An assistant suggests a library that fits the problem, with a working example, without knowing whether it is maintained, whether it was published last week, or in some cases whether it exists at all — attackers register the plausible names that assistants invent. More dependencies enter projects, with less thought per dependency, and the person reviewing the suggestion is often not equipped to tell the difference.
Where CodeControl fits
This is the problem CodeControl is shaped around. It connects to your GitHub once and then checks continuously, so nobody has to remember. Every pull request gets a secret-scanning check on the lines it adds, so a committed key is a failing check rather than a rotation. Dependency findings arrive within a minute of a push and are re-checked every night: each one shows the chain that pulled the package in, opens with the two or three items that are actually being exploited, and comes with the commands that fix them — plus a button that copies the whole thing as a prompt for the AI assistant the developer is already using.
The developer carries on solving the task in four minutes. The checking happens somewhere that does not cost them anything.