Update your dependencies with a coding agent, one package at a time
Update one package per commit, read the changelog, run the tests, and stop when something breaks instead of breaking the whole app.

Your app has been live for months and the versions in your lockfile are the same ones from the day you shipped. Some of those packages have had security fixes since then, and you do not have them. The fix is not a big update night; it is a routine where your coding agent upgrades one package at a time, reads what changed, runs the tests, and stops the moment something breaks, so you always know exactly what to roll back.
That is the whole idea: small, reversible steps. Small changes make review and fault isolation easier. They do not guarantee a faster upgrade or a working rollback; coupled dependencies may need one documented compatibility group.
Why "update everything" in one commit is the trap
When one big upgrade breaks the app, you are stuck. Dozens of packages moved at once, each with its own changelog, and nothing tells you which one did it. A bundled commit makes selective rollback harder, although a reviewed follow-up change or partial revert can still be possible. Dependency relationships and lockfile state must remain consistent.

It gets worse when the break is quiet. A dependency updates a rounding rule, a date format, an auth default, and nothing throws an error at all; the app just starts returning the wrong total and you notice weeks later with no idea when it changed. A single giant commit gives you no clean way to trace that back.
So it is tempting to give up and pin every version forever. The packages rot, the security debt compounds, and the next major jump is bigger and scarier than the last one. One package per commit turns a terrifying afternoon into a boring one, and boring is exactly what you want here.
See what is actually out of date
Before touching anything, get a list. In a Node project, npm outdated shows each package's current version, the newest version your declared range allows, and the latest release (npm docs). In Python, pip list --outdated does the same job (pip docs).
Sort by risk, not by age. Security fixes come first: npm audit reports known vulnerabilities in what you have installed and what fixing them takes (npm audit), and pip-audit is the Python equivalent (pip-audit). A patched vulnerability beats a new feature every time, so clear those before you touch anything cosmetic.
Updates and new packages are two sides of the same habit, so this pairs well with checking every package your coding agent adds before you install it.
What a version number is telling you
Versions look like 2.4.1, and that shape carries meaning. Semantic versioning is the convention behind it: the first number is the major version, the middle is minor, the last is a patch (semver.org).
Roughly: for a project following Semantic Versioning with a declared public API, a patch is a backward-compatible bug fix, a minor adds backward-compatible functionality, and a major denotes incompatible API changes. Those are project promises, not proof that a release is risk-free. That last one is the one to respect. Review one package or one explicitly coupled compatibility group per change. Read release notes for every update; give major changes separate review. Watch the major note closely for the words "breaking", "removed", and "renamed".
The catch is that not every maintainer follows the convention strictly. A minor bump can still surprise you, which is why the changelog stays the real source of truth for what changed, and why you run the tests either way.
The routine that keeps you safe
The loop is the same every time, and it is deliberately unglamorous. Pick one package, upgrade it, commit it alone, run your tests and your build, click through the main flow once, then either keep it or revert it. Your lockfile is committed with each step, so the exact working set is recorded, and git revert can create an inverse commit. It can conflict with later work and does not reverse database migrations, external side effects or already-deployed behavior. Test the rollback in the actual release process.
One independently reviewable package per commit. A coupled group is an explicit exception with its compatibility reason recorded. Majors get a separate approval step. If a test fails, stop and fix or revert before moving on, and never stack the next upgrade on top of a broken one.
You do not have to do this by hand. Hand the rules to your coding agent and let it work through the list while you do something else; if you want a harness that can run your tests and commit as it goes, consider a coding-agent workspace, after checking its access and current terms.
The prompt to copy
Paste this into your coding agent when you start:
You are updating dependencies for this project. Follow these rules exactly:
- Update ONE package per commit. Never bundle unrelated upgrades.
- Before any major version bump, summarize the changelog's breaking changes
and wait for my OK before upgrading.
- After each upgrade, run the full test suite and the build, then report.
- If anything fails, STOP and ask me. Do not fix it by upgrading more.
- Preserve the lockfile and update it with the project package manager. Do not delete or broadly regenerate it to hide conflicts. Review and commit the resulting diff.
- Never run a bulk "update everything" command.
- After each upgrade, tell me the package, the old and new versions, and one
line on what the changelog said.
A checklist you can screenshot
Keep this beside you and tick one row per package:
[ ] One package, one commit
[ ] Lockfile committed
[ ] Changelog read (breaking changes noted for majors)
[ ] Tests pass
[ ] Build passes
[ ] Main flow clicked through once
[ ] Broken? stop, review the revert and its conflicts, then retest before continuing
That is it. No cleverness, no bulk commands, no all-nighter. Just one clean commit at a time until the list is empty.