Why Does AI-Generated Code Pull in Unknown Dependencies?
How AI Optimizes for Speed Over Your Project's Long-Term Health

In the next 3 minutes:
AI optimizes for working code immediately, not for your project's dependency footprint or constraints
Audit generated imports the same way you'd review a vendor contract before signing it
Taking five minutes to understand why an AI chose a library beats months of hidden technical debt
---
Every Import Is an Architectural Decision You Might Not Be Making
You ask an AI coding tool for a retry wrapper. It hands you something clean, well-structured, and working on the first try. Tests pass. You merge and move on. Six months later, you're staring at a lockfile full of packages you never consciously evaluated, fielding questions from security about transitive dependencies you couldn't name if your job depended on it.
This is the dependency spiral, and AI-assisted development has made it faster and quieter than ever.
Why AI Tools Default to Libraries
Understanding the pattern requires understanding what's actually happening when an AI generates code. These tools don't reason about your specific constraints — your maintenance horizon, your security posture, your team's capacity to absorb new dependencies. They pattern-match against the codebases, tutorials, and Stack Overflow threads that dominated their training data.
Popular libraries dominate that training data for overlapping reasons: they're common, well-documented, and the code written around them tends to be cleaner. From the model's perspective, "popular," "well-documented," and "confidently generated" all collapse into the same signal. A mature library has had its rough edges filed off over years of production use. Reaching for it is the shortest path to handing you something correct and readable on the first attempt.
The trap is that "works on the first try" and "right for your codebase" are different questions, and the tool only has visibility into the first one. It's not weighing your situation. It has no view of your maintenance horizon at all. So when you ask for date formatting, you get `arrow`. When you ask for config loading, you get three transitive packages you never evaluated. The asymmetry is structural: the AI optimizes for the immediate task, and you're the one who lives with the lockfile.
The Hidden Cost That Shows Up Later
The reason this problem persists is that the cost is invisible at the moment the decision gets made — and by the time it surfaces, nobody remembers making the decision at all.
Every unexamined dependency is a decision nobody made, sitting in your codebase with a bill attached. That bill comes due in a few predictable ways: upgrade cycles where a transitive package breaks something two layers removed from anything you wrote; security audits where you have to explain the provenance of packages that arrived uninvited; and the slower, harder-to-measure cost of a codebase that nobody fully understands anymore.
Code reviewers routinely spend four minutes debating a variable name and skim right past an import line that just added a new surface to the supply chain. A new function gets scrutinized; a new dependency slides through. It compounds quietly until you're looking at what feels like an archaeological dig rather than a codebase.
The question worth asking before accepting any AI-suggested dependency is simple: what am I buying, and what's the smallest version of it? For cryptography, date math, and HTTP — buy it. Those domains are genuinely hard, and a mature library carries years of edge cases you'd otherwise discover in production. But if it's twenty lines of retry logic that could be written inline, and the package brings nine transitive dependencies along for the ride, you're paying a maintenance and audit bill for convenience you could have had for free. The AI won't make that distinction unprompted. The follow-up question — "what does this pull in, is there a lighter option?" — takes ten seconds and changes the entire shape of what you get back.
Setting the Guardrails Before You Need Them
The mental model that actually helps is treating an AI coding tool like a capable senior engineer who just joined the team. Smart, fast, genuinely useful — but doesn't know your house rules yet. The fix is to write the rules down somewhere the tool can see them.
A `CLAUDE.md` file at the repo root is one of the more practical approaches: a few specific lines about dependency expectations. Prefer the standard library. Flag any new package and offer an inline alternative. Here are the HTTP and date libraries already in use — pick one or make a case for something else. That file changes the default shape of what gets generated before the conversation even starts.
The other move is negotiation rather than dictation. "Add retry logic" is an open invitation to reach for a library. "Add retry logic — here are the two libraries already in our package file, pick one or write it inline, and tell me the tradeoff" is a different prompt entirely. It creates a decision point instead of a fait accompli, and it gives the tool room to flag cases where going inline would genuinely be painful.
For the habit to survive a deadline, it can't rely on individual discipline alone. Two machine-enforced practices are worth implementing this week: a `CODEOWNERS` entry on the dependency file so any change routes to a specific reviewer, and a CI check that fails the build if the lockfile grew without a note in the pull request. Then the conversation happens by default rather than by virtue of someone remembering to have it. The third piece is cultural — making "why this dependency?" a normal code review comment, asked in the same tone as any other question about the code, not as an inquisition.
The Default Nobody Set
Underneath all of this is a defaults problem. Every codebase has an implicit answer to "should we add this package?" If nobody set that default deliberately, the answer is yes — because yes is the path of least resistance for the AI, for the developer under deadline, and for the reviewer scanning a diff at four in the afternoon.
Flipping the default to "no, unless" — and writing that down somewhere both the tool and the team can read it — is the whole intervention. It's not about hand-rolling everything. Homegrown date handling will be wrong in ways a mature library stopped being wrong about a decade ago. It's about ensuring that someone actually made the call, rather than outsourcing your dependency policy to whatever happened to be popular in a model's training data.
The lockfile diff deserves the same attention as the code diff. A quick scan at review time — did anything show up that wasn't consciously added? — is two minutes that separates a codebase you understand from one that quietly grew a second nervous system while nobody was paying attention. Six months from now, the difference shows.
---
If you want to hear these ideas explored in conversation, check out the Claude Code Conversations with Claudine radio show. Available on all major podcast sites.

