Dependency upgrades fail in the same places: deprecated APIs the type checker can’t see, behavior changes inside functions whose signatures didn’t move, peer-dependency conflicts that resolve quietly into the wrong version, and runtime regressions that no unit test catches. AI agents handle this well — but only if you give them the structure: the CHANGELOG, the breaking-change list mapped to your code, and a fix loop that refuses to drift into refactoring.
What this tutorial solves
You inherit a project where a critical dependency is twelve majors behind, or a security advisory drops at 5pm Friday and the patch lives behind two breaking releases. CI is green but you do not trust the version pin. This workflow lets one developer with an AI agent do the upgrade safely in one focused session — and ship a PR with a CHANGELOG-mapped diff your reviewer can actually verify.
Who this is for
Maintainers staring at “this is 12 majors behind” situations, anyone who has been burned by npm update and now refuses to run it, devs handed an inherited project with a 4-year-old dependency tree. Also useful for one-off security-driven upgrades where the patched version is two majors ahead of yours.
When to reach for it
A specific dependency is several majors behind. CI passes but you do not trust the version pin. A security advisory is forcing the upgrade. You are picking up an abandoned internal tool and the lockfile is older than the company’s current ESLint config. Run this before you touch anything else.
When this is NOT the right tool
Framework migrations (Vue 2 to 3, React 17 to 19, Angular major-major, Rails major-major). Those need a dedicated migration plan with codemods, phased rollout, and often a feature freeze — not just an upgrade run. Also skip for tiny prototype repos where re-creating the project from scratch on current versions is faster than upgrading.
Before you start
- Confirm CI is green on main. If main is red, fix that first; otherwise upgrade failures and pre-existing failures mix and you cannot tell them apart.
- Have a working smoke test plan: the three or four manual paths that exercise the dependency you are upgrading. Type checks pass; smoke tests find the real regressions.
- Decide your upgrade boundary: just this one package and its required peer-dep bumps, or also adjacent packages? Default to “one package at a time.”
- Have a rollback plan: keep the old lockfile available and know your branch protections.
Step by step
- Branch from green main. Run the full test suite. Confirm baseline pass. If you cannot get baseline green, stop and fix baseline first.
- Read the CHANGELOG with the AI. Prompt: “I want to upgrade
\{package\}from\{x\}to\{y\}. Read its CHANGELOG and migration guide for that range. List every breaking change that could affect a TypeScript codebase using [list the APIs you use].” Save the list. - Map breaking changes to your code. For each breaking change, ask: “Find every file in this repo that uses the affected API. Give file:line references.” Click through to verify; an agent that lists files with no real matches is hallucinating.
- Upgrade the package.
npm install package@yor equivalent. Re-resolve the lockfile. Do not touch any other dependencies in the same commit. - Run tests. Catalog failures. Do not start fixing yet. List every failure: which test file, which assertion, which line of source.
- Cross-reference failures with the breaking-change list. Each failure should match an item from the list. If a failure does not match, you missed a breaking change — go back to step 2 and re-prompt with the failure as evidence.
- Fix failures one by one. For each fix, the AI should produce a minimal diff that adopts the new API, not a refactor of surrounding code. Prompt: “Is this the minimal change, or am I adopting a new pattern that doesn’t fit the rest of the codebase?”
- Run the full test suite plus manual smoke test. Type checks pass is not enough. Run the app, exercise the smoke paths, watch the network and console for warnings.
- Commit with a CHANGELOG-mapped message. Each commit names which breaking change it addresses. Reviewer can verify diff vs. CHANGELOG.
Breaking-change mapping prompt
Package: \{name\}
Upgrade: \{from-version\} -> \{to-version\}
Codebase: TypeScript 5, Node 22, [framework]
Read the official CHANGELOG and migration guide for this range.
Return a table:
| Breaking change | Affected API | file:line in this repo | Suggested fix |
Only include breaking changes that affect this codebase. Skip
changes to features we do not use. Cite the CHANGELOG section
for each row so I can verify.
First-run exercise
- Pick a single dependency that is 1-2 majors behind. Not the riskiest one.
- Run all nine steps end to end on that single dependency.
- Time the run. Note which steps the AI got right and which needed re-prompting.
- The second run on a riskier dependency will be much faster because you’ll know your agent’s reliability per step.
Quality check
- Does every test failure in your catalog map to a known CHANGELOG entry? Unmatched failures mean undiscovered breaking changes.
- Did you smoke-test in a running app, not just CI? Type checks miss runtime regressions, especially silent ones (logging changes, default option flips).
- Did the AI add unrelated refactors? Revert those — they belong in a separate PR.
- Did the lockfile resolve cleanly, or did it pull in a major version of a transitive dependency you did not approve? Pin if needed.
How to reuse this workflow
- Save the breaking-change mapping prompt as a reusable template; only the package and version range change.
- Keep an upgrade log per project: which packages, which versions, which failures, how long it took. Patterns emerge — some packages are reliably painful at certain version transitions.
- Run minor patch upgrades via Dependabot or Renovate automated PRs; reserve this manual AI workflow for majors and risky minors.
Recommended workflow
React Router 5 to 6: list breaking changes (Switch to Routes, useHistory to useNavigate, etc.) -> upgrade -> 11 test failures -> fix each mapped to a known breaking change -> ship. If your app deploys to Firebase Hosting, run the AI Firebase deploy checks workflow right after — major upgrades often break SPA rewrite rules and function regions that local tests won’t catch.
Common mistakes
- Skipping the CHANGELOG step. AI will guess breaking changes from training data, which may be wrong for your specific version. Always ground the agent in the published CHANGELOG.
- Letting AI make stylistic refactors during the upgrade. Each refactor adds risk to a PR that should be boring. Save them for separate work.
- Upgrading multiple packages at once. When tests break, you cannot attribute failures cleanly. One package per PR.
- Not running smoke tests on a running app. Type checks pass; the app loads blank because a default prop flipped. Manual smoke is non-optional.
- Trusting transitive dependency resolution. If lockfile pulls in a new major of a transitive, pin it explicitly until you’ve reviewed its CHANGELOG too.
- Calling it done at green CI. Production has runtime configurations CI doesn’t replicate — deploy to staging first.
Advanced tips
- For framework upgrades, ask: “What is the recommended migration path for
\{x\}to\{y\}? Are there codemods or migration scripts published by the maintainer?” Use them. - For peer-dependency hell, ask: “What is the minimum compatible set of versions to upgrade together?” Then upgrade as a coordinated unit, not piecemeal.
- For repeatedly painful packages, build a snippet library of past fixes. Many packages reuse the same breaking-change patterns across major versions.
- For monorepos, upgrade in the smallest workspace first to learn the shape of the change, then propagate.
Output checklist
- Branched from green main with all tests passing baseline.
- CHANGELOG breaking changes mapped to specific files in your repo.
- All test failures attributable to a known breaking change.
- Smoke test on a running app passes.
- No stylistic refactors mixed into the upgrade commits.
- Lockfile reviewed and committed.
FAQ
- Use Dependabot too?: Yes — for safe patch upgrades and minor bumps. AI workflow shines on majors, risky minors, and packages with known migration pain.
- What about deprecation warnings?: Treat as homework, not a blocker. Schedule the cleanup as a separate PR within 2-4 weeks. Letting deprecations pile up creates the next “12 majors behind” problem.
- How do I handle indirect / transitive dependencies?: Audit the lockfile after the upgrade. If a transitive jumped a major you did not authorize, pin or override it explicitly.
- What if the AI insists a breaking change is irrelevant when it actually is?: Show it the failing test output and ask: “Reconcile this failure with the CHANGELOG entry you said didn’t apply.” Agents correct themselves when given the contradiction.
- Can I parallelize across multiple agents?: One agent at a time on the same branch. Parallel agents on the same upgrade cause merge conflicts and lose the CHANGELOG-to-failure trace.
- Does this work for npm, pip, cargo, gem?: Yes for all of them. The CHANGELOG-grounding step is language-agnostic; only the install command and test framework details change.
Related
- AI refactor workflow
- AI agent code review workflow
- AI rollback workflow
- AI Debugging Workflow — From Stack Trace to Fix
- AI Migration Prompt Workflow — Framework / Language Migrations
- AI Coding in a Monorepo — Workflow That Doesn’t Drown in Context
- AI Spec-to-Code Workflow
- AI Test Generation Workflow — Tests You Can Actually Trust
- AI Firebase deploy checks
Tags: #AI coding #Tutorial #Workflow