To upgrade dependencies with AI, let the agent do the reading and the repetitive edits, and keep the safety rules for yourself: one major upgrade per branch, the migration guide read before any code changes, and the test suite as the only proof that it worked. Coding agents such as Claude Code, Codex and Gemini CLI are good at the tedious part of upgrades: renaming an API in eighty files, adapting test setup to a new runner, explaining a cryptic peer dependency error. They are bad at one thing that matters a lot here: they remember the old version of the library.
This guide walks through a workflow that works for a small app and a large monorepo alike: inventory, triage, batching the easy updates, isolating the hard ones, codemods, lockfiles, verification, bot PRs and security advisories. Examples use npm and pnpm, with Python equivalents where they differ.
Why dependency upgrades go wrong with agents
Upgrades look simple, which is why they go wrong. The usual failure modes:
- Stale knowledge. A model’s training data stops at some point. Ask it to move to a version released after that and it will confidently write code for the version it knows.
- Scope creep. “Upgrade React” turns into React, the router, the test library, ESLint and three plugins, all in one diff.
- Moving the goalposts. A test fails after the upgrade, so the agent updates the snapshot, loosens the assertion or skips the test.
- Silencing the installer. A peer dependency conflict gets “fixed” with
--forceor--legacy-peer-depsinstead of finding versions that actually fit together. - Lockfile chaos. The agent deletes the lockfile to make an install error go away, and hundreds of transitive packages change at once.
Every step below is there to block one of these.
Step 1: take an inventory
Start with facts, not a prompt like “update everything”. Each ecosystem has a built-in command that lists what is behind:
npm outdated # Current, Wanted (highest in your range) and Latest
pnpm outdated # add -r in a workspace
pip list --outdated # for the active Python environment
Add the security view:
npm audit
pip-audit # PyPA tool: pip install pip-audit
Then hand the output to the agent and ask for a triage, not changes:
Here is the output of npm outdated and npm audit. Don't change anything yet.
Make a table with: package, current, wanted, latest, update type
(patch/minor/major), whether it's a direct or dev dependency, and a link to
its changelog or releases page. Flag anything with a security advisory.
Then propose an order: what can be batched, what needs its own branch.
This is a read-only job, so it’s safe to run in a planning mode or with a read-only permission setting. The table becomes your plan, and it shows you early which upgrades are actually big.
Step 2: batch the easy updates
Patch and minor versions are supposed to be backwards compatible. In practice they usually are, and the test suite catches the rest. Do them together, on one branch:
git switch -c deps/minor-patch
npm update # stays inside the ranges in package.json
With pnpm, pnpm update does the same, pnpm update --interactive lets you pick, and pnpm update --latest crosses major versions, so keep that one for Step 3. Then:
Run the full test suite, the type check and the build. If something fails,
find which package caused it and fix the code, not the tests. If a fix
needs more than a few lines, revert that one package to its previous
version and list it for a separate upgrade.
That last sentence matters. It keeps the batch small and boring, and moves anything surprising into its own branch.
Step 3: one major upgrade per branch
A major version means breaking changes by definition. Give each one its own branch, and if you want to run several at the same time, its own folder with a git worktree:
git worktree add ../app-vite -b deps/vite-major
git worktree add ../app-eslint -b deps/eslint-major
Now one agent upgrades the build tool, another the linter, and your main folder stays untouched. If one upgrade turns into a swamp, you delete that worktree and branch and lose nothing else. Git worktrees for parallel agents covers the setup in more detail.
Two rules for majors:
- One major step at a time. Going from version 3 to 6 in one jump stacks three sets of breaking changes in one diff. Go 3 → 4, get green, commit, then 4 → 5.
- Upgrade related packages together, and nothing else. A framework and its official plugins often have to move in lockstep. That’s fine. Unrelated packages wait.
Step 4: read the migration guide before touching code
This is the step that fixes the stale-knowledge problem. Before the agent edits anything, it should read the real documentation for the exact version you’re moving to:
We're upgrading eslint from 8 to 9. Before editing any file:
1. Read the official migration guide and the changelog for 9.0.0.
2. List each breaking change that applies to this repo, with the files
it affects (search the code to confirm).
3. Say what you will do for each one.
Use the installed package's docs and type definitions as the source of
truth, not what you remember about older versions. Wait for my OK.
If your agent can browse, it fetches the guide itself; if not, paste the relevant parts or point it at the release notes in node_modules/<package>/CHANGELOG.md when the package ships one. An MCP documentation server helps here too; see the best MCP servers for coding.
The list it produces is also your review checklist later: every item should map to a change in the diff.
Step 5: let the codemod go first
Many frameworks ship codemods: scripts that rewrite code mechanically for a new version. A codemod is deterministic and the agent is not, so let the codemod do everything it can and give the agent what’s left.
Two well-known examples:
# Next.js: upgrades Next.js, React and React DOM and runs the codemods
npx @next/codemod upgrade major
# Angular: updates packages and runs their migrations
ng update @angular/core @angular/cli
As of October 2026, the Next.js docs note that @next/codemod upgrade runs non-interactively and accepts every default when stdin isn’t a terminal, which is the case when an agent runs it. If you want to choose which codemods apply, run it yourself first and hand the result to the agent.
After the codemod, commit, then ask the agent to handle the remaining items from Step 4: the cases the codemod marked or skipped, custom wrappers around the old API, test setup, and config files. Keeping the codemod output and the agent’s edits in separate commits makes review much easier. The same idea applies to bigger migrations; refactor legacy code with AI and migrate JavaScript to TypeScript with AI go deeper into step-by-step migrations.
Step 6: treat the lockfile as part of the change
The lockfile is what makes “works on my machine” mean anything. A few rules worth putting in AGENTS.md or CLAUDE.md:
## Dependencies
- Never delete package-lock.json / pnpm-lock.yaml to fix an install error.
- Don't use --force or --legacy-peer-deps without asking.
- Install with npm ci in CI; commit the lockfile with the package.json change.
- Don't add new dependencies during an upgrade task.
When an install fails on peer dependencies, ask the agent to explain the conflict instead of forcing it. npm explain <package> (alias npm why) shows why a package is in the tree and who requires which version; npm ls <package> shows every copy. Often the real fix is upgrading the plugin that pins the old peer, which belongs in the same branch.
When you review, look at the lockfile diff too. A minor bump of one direct dependency that changes three hundred transitive entries deserves a question.
Step 7: verify with your own checks
“It installs” is not done. Define done in the prompt, in terms the agent can check:
Done means: npm ci succeeds, npm test passes, tsc --noEmit passes,
npm run build passes, and no existing test file was changed except
where the migration guide requires it (explain each one).
If your test suite is thin around the library you’re upgrading, add a few tests first, on the old version, and keep them green through the upgrade. For UI libraries, a quick check in a real browser catches what unit tests miss: an agent with a browser can open the main pages and report console errors. For build tools, compare the build output size before and after; a doubled bundle is a bug even if every test passes.
Review the diff with three questions: did any test change, did any public behaviour change, and does every edit map to an item on the breaking-change list?
Bot PRs: Dependabot and Renovate plus an agent
Dependabot and Renovate are good at opening upgrade PRs on a schedule. What they can’t do is fix the code when the upgrade breaks something. That’s the part an agent can take.
A minimal Dependabot config that groups the easy updates into one PR:
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
groups:
minor-and-patch:
update-types:
- "minor"
- "patch"
The Renovate equivalent starts from its recommended preset:
{
"extends": ["config:recommended"],
"packageRules": [
{ "matchUpdateTypes": ["minor", "patch"], "groupName": "minor and patch" }
]
}
When a bot PR goes red, the loop looks like this:
gh pr checkout 123
gh pr checks 123 # find the failing run
gh run view <run-id> --log-failed # the log of its failed jobs
This is a Dependabot PR that bumps zod to the next major. CI fails with the
log below. Find the root cause, read the release notes for the new version,
and fix our code so it works with the new version. Don't pin the old version
and don't change tests to hide the failure. Run the tests locally, then
summarize the change before you push.
One detail: once you push your own commits to a Dependabot branch, Dependabot stops rebasing that PR automatically, and @dependabot recreate would throw your fix away. Merge it soon or rebase it yourself. For the general version of this loop, see auto-fix CI failures with AI.
Security advisories
npm audit lists known vulnerabilities in your tree. A few things that keep it useful:
- Focus on what ships.
npm audit --omit=devleaves out dev dependencies, and--audit-level=highmakes the command fail only at high or critical severity, which is a sensible CI gate. npm audit fixvs--force. Plainaudit fixstays inside your declared ranges. With--forceit may install semver-major versions; treat that as a Step 3 upgrade, not a quick fix.- Transitive packages. When the vulnerable package is deep in the tree and its parent hasn’t released a fix, npm’s
overridesfield inpackage.json(or pnpm’spnpm.overrides) can force a patched version. Ask the agent to add a comment or an issue explaining why, so the override is removed later. - Ask whether it’s reachable. Have the agent check if your code actually uses the vulnerable function. That doesn’t mean you skip the fix, but it tells you whether it’s urgent today or can go in the weekly batch.
For Python, pip-audit reports the same kind of findings for an environment or a requirements.txt, and pip-audit --fix tries to upgrade the affected packages. The wider topic of what agents themselves should be allowed to install is covered in AI coding agent security.
Common mistakes
- “Update all dependencies to latest” in one prompt. You get one giant diff and no way to bisect it.
- Skipping the migration guide. The agent writes code for the version it remembers.
- Letting tests change silently. An updated snapshot is fine when the guide explains the change; otherwise it’s hiding a regression.
- Forcing the installer.
--forcetoday is a broken install for the next person. - Merging parallel upgrade branches without testing them together. Two green branches can be red once combined; a merge queue tests each one on top of the latest main.
Doing it in AgentVera
AgentVera is a desktop app that runs coding-agent CLIs like Claude Code, Codex and Gemini CLI side by side, with git built in. A few of its features map directly onto this workflow:
- A worktree per major upgrade. Create a worktree from the Git panel and start an agent in it with one click; run the Vite upgrade in one pane and the ESLint upgrade in another.
- A weekly dependency rule. Automations (Pro) can start an agent on a cron schedule in its own worktree. One of the built-in playbooks is “update dependencies (minor and patch only)”, and the result can land as a PR or go into the merge queue.
- Red CI to an agent. The CI/CD center lists GitHub Actions, GitLab CI and Jenkins runs; on a failed run, “Fix with an agent” opens a Claude agent with the run details and the log tail of up to three failed jobs. An automation can also start an agent when CI goes red on branches you choose.
- Verify against your definition of done. Write “tests, type check and build pass; no test file changed” in your message. Verify turns it into criteria and, when the turn ends, a separate verifier that can’t edit files checks each one with evidence.
- Review and merge queue. Code review reads what the agent changed with a second model, and the merge queue lands upgrade branches one at a time on top of the latest target, with your check command run on the merged result.
Wrapping up
Upgrading dependencies with AI works when you keep the shape of the work small: inventory first, easy updates in one batch, each major on its own branch, the migration guide read before the first edit, the codemod before the agent, and the tests as the judge. The agent saves you the reading and the hundred small edits; the process keeps those edits honest. When the upgrade is done and you want to make the app faster as well, read how to optimize code performance with AI.
To try it on your own repo, download AgentVera.