
Mitigating Dependency Bloat and Vulnerability Introduced by AI Coders
I've seen AI coding tools ship useful code and quietly add three packages to do something the platform already handled. That is not just untidy. It increases attack surface, adds update work, and makes reviews harder than they should be.
Why AI Coders Inflate Dependencies
AI-generated code usually optimizes for “works now,” not “stays boring and safe later.” If a prompt mentions parsing, retries, formatting, hashing, debouncing, or date handling, the model often reaches for a package because that pattern is common in its training data.
That default is rough on teams. One helper can pull in a chain of transitive dependencies, each with its own release cycle, install scripts, and vulnerability history. The code may look shorter, but the system becomes harder to reason about.
I usually treat every new dependency as a design decision, not an implementation detail.
What Actually Breaks When the Dependency Graph Grows
Security exposure from transitive packages
The package you added is rarely the whole story. The bigger risk is what it brings with it.
A package tree can hide:
- vulnerable transitive packages
- install-time scripts
- unreviewed optional dependencies
- abandoned maintainers
- packages with broad filesystem or network access during tooling
The bug is often not “this package is malicious.” The bug is that no one reviewed what it can reach, when it runs, or why it needs to exist at all.
Bundle size, cold starts, and maintenance drag
Dependency bloat also hurts operationally:
- larger frontend bundles
- slower serverless cold starts
- longer CI install times
- more lockfile churn
- more patching when advisories land
If you ship JavaScript in browsers, every extra package can become user-visible. If you deploy serverless code, the cost shows up in startup latency and package size. With AI coding tools, this becomes a pattern because the tool does not feel the maintenance cost later.
A Practical Review Loop for AI-Generated Code
Check whether an existing standard library API already solves it
Before accepting a new package, ask a simple question: what does the platform already provide?
A lot of common AI-suggested dependencies can be replaced with built-ins:
fetchinstead of request wrappers for simple casesURLandURLSearchParamsinstead of custom query helperscrypto.subtlefor hashing and signatures in supported environmentsIntlfor formatting instead of date or number helper packagesstructuredClonefor deep copies in supported runtimes
If the built-in does the job, prefer it. Built-ins are easier to patch, easier to audit, and less likely to surprise you.
Compare direct dependencies against transitive imports
When reviewing code, do not stop at package.json. Check the actual import graph.
A package that looks small can drag in a lot of unrelated code. You want to know:
- how many direct dependencies were added
- how many transitive packages they introduce
- whether they are runtime or dev-only
- whether they are used in server code, client code, or both
A quick example:
| Layer | What to check | Why it matters |
|---|---|---|
| Direct dep | Why was it added? | Confirms necessity |
| Transitive deps | How many and by whom? | Reveals hidden risk |
| Runtime path | Is it in prod? | Separates dev noise from real exposure |
| Install scripts | Does it execute code on install? | Flags supply-chain risk |
Review install-time and runtime behavior separately
A package can be harmless at runtime and still be risky during installation.
I review these two questions separately:
- Does it execute anything on install?
- What does it do when your app starts?
That split matters. Some packages only need a build step, while others run code immediately in production. An AI coder will not make that distinction for you unless you force it into the review process.
JavaScript Examples of Common Dependency Mistakes
Replacing small utility packages with built-ins
This is the kind of change AI tools often suggest without asking whether the dependency is necessary.
// Instead of adding a date helper package for simple formatting:
const formatDate = (date) =>
new Intl.DateTimeFormat("en-US", {
year: "numeric",
month: "short",
day: "2-digit",
}).format(date);
// Instead of a query-string helper:
const params = new URLSearchParams({ page: "2", sort: "new" });
const url = new URL("/api/items", "https://example.com");
url.search = params.toString();That is not about purity. It is about cutting down the number of places where third-party code can surprise you.
Spotting accidental framework-level additions
AI-generated code also tends to “solve” a small problem by pulling in a large framework utility or a general-purpose abstraction.
For example, a tiny UI helper should not turn into:
- a full state-management library
- a markdown engine for rendering one field
- a date library for one timestamp
- a validation framework for a single form field
If the imported surface looks bigger than the problem, stop and ask why.
Mitigation Strategies That Actually Hold Up
Dependency allowlists and lockfile review
The simplest policy is the one teams actually follow.
Maintain an allowlist of approved packages for production use. Then review lockfile changes like code, because they are code from your build system's point of view.
At minimum, check:
- new top-level packages
- unexpected version jumps
- new install scripts
- native bindings
- packages that only exist to support another package
A lockfile diff often tells you more than the AI prompt did.
Automated scanning and update discipline
Use automated scanning, but do not confuse scanning with review.
You want:
- vulnerability alerts on both direct and transitive packages
- dependency freshness checks
- license review where it matters
- regular patching windows instead of emergency drift
The point is not to eliminate risk. The point is to keep it visible and bounded.
Human review focused on trust boundaries
The most useful human review is not “does this code look clever?” It is “what trust boundary changed?”
Ask:
- Did this add network access?
- Did this add filesystem access?
- Did this move validation from server to client?
- Did this trust a new third-party parser?
- Did this introduce code that runs before the app is fully initialized?
That is where dependency bloat becomes a security issue.
A Minimal Policy for Teams Using AI Coding Tools
If your team uses AI coding assistants, keep the policy short:
- Prefer built-in APIs first.
- Require a reason for every new production dependency.
- Review direct and transitive packages before merge.
- Block install scripts unless explicitly approved.
- Scan lockfiles and update dependencies on a schedule.
- Treat dependency additions as security changes, not just refactors.
That policy is small enough to remember and strong enough to matter.
Conclusion
AI coders are good at producing working code fast. They are not good at feeling the long-term cost of a new dependency tree.
The fix is not to ban the tools. The fix is to make dependency review part of the normal path: built-ins first, imports second, transitive packages always visible, and trust boundaries reviewed by a human. If you do that consistently, AI-generated code can stay useful without quietly turning your app into a maintenance problem.


