Upgrade a Project to a New Framework Version With Claude Code

Upgrade a Project to a New Framework Version With Claude Code

Framework upgrades are the chores everyone puts off. You can upgrade a framework with Claude Code safely by asking for a read-only plan first, then letting it change the code in small steps while your old tests prove nothing broke. In this project we moved a small web API from Express 4 to Express 5 and ended with all 9 tests passing, without touching a single test file.

This is Project 19 of 20 in our Build with Claude Code series, part of our Claude Code tutorials. In Project 18 we learned to fix security issues with a plan, small fixes and tests. We use the same habits again here.

Tested with Claude Code v2.1.310 on October 27, 2026, on macOS. We tested on macOS; the Windows and Linux steps follow Anthropic’s official instructions.

What We Built

A framework upgrade is only done when the old tests pass on the new version. Claude Code can find and fix the breaking lines, but your tests decide if the upgrade worked.

ItemDetails
ProjectUpgrade a small book-list API from Express 4 to Express 5
InputA made-up app with 3 source files and 9 passing tests
OutputThe same app on Express 5.3.0, with six lines changed in one file
ToolsNode.js, npm and the built-in Node test runner
Result9 of 9 tests pass before and after; 0 test files edited
Prompts used2 short prompts, plus a git branch we made ourselves
Dark flow diagram: an Express 4 app with 9 passing tests goes through plan, upgrade and fix, and run the tests, with a retry loop, to an Express 5 app with 9 passing tests, while you work on a git branch and read every diff
The upgrade in three small steps, with you working on a branch

Why Upgrade a Framework With Claude Code?

An upgrade is mostly boring detective work. You read a list of breaking changes, search your code for each one, and fix them one by one. Miss a single line and the app crashes, sometimes only on one route that nobody visits often.

Claude Code is a good match for this job. It can read every file and edit many lines quickly.

Anthropic’s docs list refactoring old code as a common workflow. One suggested prompt is to find deprecated API usage in your codebase, which is exactly where an upgrade starts.

There is one honest limit. Claude learned about frameworks from training, and it can be wrong about a version that is brand new. That is why this project leans on tests and on reading every change, not on trust.

The Project We Started With

Everything here is made up. The app is a tiny “bookshelf” API that lists books, shows one book, adds a book, deletes a book, redirects an old URL and answers anything else with a JSON 404.

FileWhat it does
src/data.jsHolds three sample books in a list
src/app.jsAll the routes, written for Express 4.21.2
src/server.jsStarts the server on a port
test/api.test.jsNine tests that call the running API

The tests start the app on a random port and call it with the built-in fetch function. They do not know which Express version is underneath, which makes them a perfect safety net for an upgrade.

We saved this starting point as a git commit and then made a new branch with git switch -c upgrade-express-5. If the upgrade goes badly, you can switch back to your main branch and nothing is lost.

What Happens If You Upgrade Without a Plan

Before we used Claude, we wanted to see the problem for real. In a throwaway copy of the project we ran npm install express@5 and then the tests, changing no code at all.

Terminal showing express 5.3.0 installed and a PathError saying Unexpected question mark at index 18 in the route /books/:id/:format?, with 1 test run and 1 failed
Express 5 refuses to start the old app: the optional route syntax is gone

The app did not even start. Express 5 uses a newer route parser, and the old :format? way of writing an optional part is no longer valid.

All nine tests failed to run, because the file that builds the app crashed on load.

The crash message only shows the first problem. Other old calls in the app would fail one by one after you fixed that line. This is why a full list up front beats trial and error.

Step 1: Make a Plan in Plan Mode

We started Claude Code in plan mode. In this mode Claude reads files and proposes a plan but makes no edits until you approve, as described in Anthropic’s docs on planning before editing.

claude --permission-mode plan

Our prompt asked for a short, exact list so we could check it line by line.

This project uses Express 4. I want to upgrade to Express 5. Do not change any file. Read the code in src/ and list every line that Express 5 will break, one line each: the file, the old code, and the fix. Keep it short.
Claude Code in plan mode showing a table of six lines in src/app.js that Express 5 will break, with the old code and the fix for each, and a note that package.json also needs the new version
Claude’s read-only plan: six lines in app.js, each with the old code and the fix

Claude answered in about 12 seconds. It said six lines in src/app.js break, and that server.js and data.js are fine. Each row had the line number, the old code and the fix.

Old codeFix
/books/:id/:format?/books/:id{/:format}
req.param(‘id’)req.params.id
res.send(404)res.sendStatus(404)
res.json(book, 201)res.status(201).json(book)
app.del(…)app.delete(…)
app.get(‘*’, …)app.get(‘/{*splat}’, …)

Claude also added two notes. It said package.json needs the new version, and that Express 5 changes how req.body behaves when nothing was sent.

It checked our code and said the line that spreads req.body is not affected. We did not need to act on those notes, but it is good to see Claude check its own list against your real code.

Step 2: Upgrade and Fix, With Your Approval

Next we continued the same conversation in the normal permission mode, so Claude had to ask before each edit or command. The second prompt was short, because the plan already did the thinking.

Upgrade express to version 5 with npm, then fix the six lines from your list in src/app.js. Do not edit any test file. Then run npm test.

The first thing Claude asked to run was the npm install. Read the command before you say yes. Here it was npm install express@5, followed by a check of package.json.

Claude Code asking for approval to run npm install express@5 with choices Yes, Yes and do not ask again for npm install, switch to auto mode, and No
Claude asks before it runs the install command

Then came six edit requests, one for each line. Each one showed a small before and after view.

We said yes to each, but we also read each one. The first edit changed the optional route.

Claude Code edit approval showing the route line changed from /books/:id/:format? to /books/:id{/:format} with a red removed line and a green added line
The first edit: the new way to write an optional part of a route

Last, Claude asked to run npm test. Because we had told it not to edit tests, it could only fix the app.

Claude Code summary saying it upgraded Express to 5 and fixed six lines, with npm test passing all 9 tests and no test file touched, plus the diffs for app.delete and the splat catch-all
Claude reports 9 of 9 tests passing and no test file touched

Step 3: Check the Result Yourself

Never rely on the summary alone. We ran our own checks in a plain terminal: the branch name, the version in package.json, the test count, and how many test files changed.

Terminal showing branch upgrade-express-5, express ^5.3.0, 9 tests with 9 passing and 0 failing, and Test files changed: 0
Our own check: 9 tests pass, 0 test files changed

Git then showed three changed files: package.json, package-lock.json and src/app.js. The big line counts come from the lock file, which npm rewrites when dependencies change. The real code change is only twelve lines in app.js.

Dark folder tree of the project after the upgrade showing package.json changed from Express 4.21.2 to ^5.3.0, app.js with six lines fixed, and every other file unchanged including the 9 tests
The finished project: three files changed, tests untouched

The Key Code After the Upgrade

Here is the part of src/app.js that changed. The other routes, such as the list route and the redirect, stayed exactly as they were.

app.get('/books/:id{/:format}', (req, res) => {
  const book = books.find((b) => b.id === Number(req.params.id));
  if (!book) {
    return res.sendStatus(404);
  }
  if (req.params.format === 'text') {
    return res.send(book.title + ' by ' + book.author);
  }
  res.json(book);
});

app.post('/books', (req, res) => {
  const book = { id: books.length + 1, ...req.body };
  books.push(book);
  res.status(201).json(book);
});

app.delete('/books/:id', (req, res) => {
  /* same body as before */
});

app.get('/{*splat}', (req, res) => {
  res.status(404).json({ error: 'Not found' });
});

One detail is worth a second look. The old res.send(404) sent a status code, and the new res.sendStatus(404) sends the code and also its text, “Not Found”, as the body.

Our test only checks the status, so it passes. If a client of yours reads that body, check it.

Why Plan Mode Matters for Upgrades

Upgrades touch many lines, and a long session can lose track. Anthropic’s guide for large codebases says that when you plan first in plan mode, Claude writes the plan to a file, and Claude Code puts that plan back after each context compaction, so the plan survives where chat history may not. You can read the details in the large codebase guide.

Our app was tiny, so we never hit that problem. On a bigger project, a written plan is your anchor. If you want to understand the other modes, our guide to permission modes explains each one.

Try It Yourself

You can repeat this on any small project that has tests. Make a throwaway copy, put it in git, and start a new branch.

git switch -c upgrade-framework

Then ask for a read-only plan. Replace the names with your own framework and versions.

This project uses [framework] [old version]. I want to upgrade to [new version]. Do not change any file. Read the code and list every line the new version will break, one line each: the file, the old code, and the fix. Keep it short.

When the list looks right, approve the upgrade with a second prompt.

Upgrade [framework] to [new version] with npm, then fix every line from your list. Do not edit any test file. Then run the tests.

Here are some variations to try next.

  • Ask Claude to look for deprecated calls first, before you choose a version.
  • Upgrade one step at a time, for example 3 to 4 and then 4 to 5, instead of jumping far.
  • Ask Claude to explain each fix in plain English before you approve it.
  • Add a rule to your CLAUDE.md file, such as “never edit test files during an upgrade”.

Common Mistakes

  • Upgrading on your main branch. Always use a new branch, so you can walk away from a bad upgrade.
  • Skipping the plan and fixing crashes one at a time. You waste many runs and still miss quiet changes.
  • Letting Claude edit the tests to make them pass. Say clearly that test files are off limits.
  • Having no tests at all. Without them you cannot prove the upgrade worked. Our project on writing tests fixes that first.
  • Trusting the summary. Run the tests yourself and read the diff.

Pro Tips

  • Ask for one line per problem. Short lists are easy to compare with the official upgrade notes of your framework.
  • Keep the upgrade prompt small and exact. The plan already holds the details.
  • Read the lock file change only briefly. It is large by nature, so focus on package.json and your source files.
  • Check places tests do not cover, such as response bodies and headers, by hand or with a few new tests.
  • Do not paste real passwords or customer data into the session, and see our page on privacy and security for how your data is handled.

Cheat Sheet

Dark cheat sheet card with the prompts used in this project and the six Express 5 fixes: optional route, req.params, sendStatus, status then json, app.delete and the splat catch-all
The prompts and the six fixes from this project

What the Official Docs Say

We backed our steps with Anthropic’s own documentation. The common workflows page has a refactoring section that starts with the idea of updating old code to modern patterns.

Claude Code docs page Refactor code section with the sentence about updating old code to use modern patterns highlighted and the suggested prompt find deprecated API usage in our codebase
The docs suggest finding deprecated API usage as the first refactoring step

The same page explains plan mode, which is the safe way to start any change that you want to review before it touches your files.

Claude Code docs page Plan before editing with the paragraph about plan mode making no edits until you approve highlighted, and the command claude --permission-mode plan
Plan mode reads and proposes, and makes no edits until you approve

The guide for large codebases adds why a written plan helps on long jobs.

Claude Code docs page Scope and plan changes that span packages with the Plan before editing bullet highlighted, which says the plan file is re-injected after each compaction
A plan written to a file survives a long session

Frequently Asked Questions

Can Claude Code upgrade a framework for me?

Yes, it can find the code that the new version breaks and edit it. In our test it fixed six lines and moved the app from Express 4 to Express 5 with every test still passing. You still need to review the changes and run your tests.

Should I upgrade on a branch?

Yes. A branch keeps your main code safe, and you can throw the branch away if the upgrade goes wrong. It takes one command and costs nothing.

What if my project has no tests?

Write a few first. Even five tests for your main routes give you a way to see if anything broke. Claude Code can help write them, as our tests project shows.

Can Claude Code be wrong about a new framework version?

It can. Claude may not know every change in a very new release. Compare its list with the framework’s official upgrade notes, and let your tests catch the rest.

Do I have to use plan mode?

No, but it is the safest first step. You get a full list of changes without a single file being edited, and you can say no before anything is touched.

Conclusion

You just saw how to upgrade a framework with Claude Code on a small project: use a git branch, ask for a read-only plan, approve the fixes one by one, and prove the result with the same tests you started with. Express 5 broke our app on the first run, and six small edits fixed it, with 9 of 9 tests passing and no test file changed. Keep your own judgment in the loop, read every diff, and see our Claude AI overview when you want to go further.

Previous in the series: Project 18: Find and Fix a Security Issue With Claude Code

Next in the series: Project 20: Build an App With an AI Feature Using Claude Code and the Claude API.

Newsletter

New AI tools in your inbox

The best new tools and the AI news that matters, in one short email. Unsubscribe any time.

Free · No spam · Unsubscribe any time

✓ Looks like you’re subscribed

Loading Google’s sign-up form…

Looks like you’re already subscribed

Thanks for reading! New AI tools and the AI news that matters will land in your inbox.

Not subscribed yet? Refresh the page to load the sign-up form.

The sign-up form couldn’t load. If you use an ad or content blocker, allow news.google.com and reload the page.

Free instant check before you pay. Takes a few seconds.

Scroll to Top