Skip to main content

Command Palette

Search for a command to run...

Your Agent Added a New Dependency to Solve What Your Standard Library Already Handled

The agent finished fast and the PR looked clean. Then package.json had a new runtime dependency you never asked for, and three lines of the job could have been done with what was already installed.

Updated
•3 min read•View as Markdown

The agent finished fast and the PR looked clean. Green checks, a tidy diff, a passing build. Then you open package.json and there it is: a new runtime dependency you never asked for, pulled in to do three lines of work that something already on disk could have handled.

This is one of the quietest failures in AI-assisted coding. It does not break the build. It does not produce a test failure. It quietly grows your supply chain, your install time, and your attack surface, one small convenient package at a time.

Why the agent reaches for a new package

An agent optimizing for a fast, correct answer will take the shortest path it knows. If its training memory contains a well-loved library for parsing dates, flattening nested objects, or retrying HTTP calls, it will reach for that package before it checks whether your repo already has a util for it. Adding a dependency feels cheap to a model that does not feel the install.

It also has no model of your dependency budget. It does not know that your security team flags every new runtime dep, that your install takes four minutes and grows with every entry, or that the three-line task it just solved is now a piece of software your team has to keep patched for the next three years.

The three versions I keep seeing

First, the reinvented standard library. Date formatting, deep equality, a debounce, a retry wrapper. The language already ships a reasonable version, or your repo has three near-identical helpers, and a fourth small package shows up anyway.

Second, the solved-by-another-tool case. You already depend on a validation library, an HTTP client with a retry policy, or a logging setup. The agent adds a second tool for the same job because it solved the sub-task in isolation instead of looking at what the codebase already does.

Third, the almost-right abstraction. The package solves the problem, but only because it also pulls in transitive dependencies, a native build step, or a behavior that subtly differs from how your codebase already handles the edge case.

The cheap way to catch it

I stopped trying to enumerate forbidden packages. Instead I added one standing instruction to the task: before adding a dependency, search the repo for an existing helper and confirm the standard library cannot do the job; if you still need one, name it and say why in the PR description.

That single line catches most of it. The agent either finds the existing util, uses the stdlib, or proposes the dependency out loud where a human can say no. The ones that still slip through get caught by the diff I actually read: package.json and package-lock.json are the first files I open on an agent PR, before the logic.

For the kind of work where the spec is the contract, this is the same instinct. We ended up keeping API and contract checks local in Powerduck rather than bolting on another hosted service, because the source of truth was already on disk. The same bias applies to dependencies: reach for what you have before you ask for more.

The question worth asking on every agent PR

Did this change actually need anything new? If the answer is a dependency, a config key, or a service the codebase did not use yesterday, that is not noise to skim past. It is the part most likely to outlive the feature itself.

More from this blog

P

Powerduck Blogs

144 posts

Essays on local-first API tooling, OpenAPI contracts, MCP, and agentic coding failures. We build Powerduck, a local-first OpenAPI studio where the spec stays the source of truth. Topics: AI code review trust, testing AI-generated code, context engineering, and what actually breaks when AI writes your code.