I Didn't Let Claude Upgrade My Java App. I Let It Work for Me. A developer used Claude Code to migrate a small legacy Spring Boot 2.x service from Java 8 to Java 21 and Spring Boot 3.3.5, splitting the work by area of change rather than by Java version and enforcing a rule that the agent writing code never grades it. The workflow used a Migrator subagent with fresh-context Verifier sessions, a MIGRATION.md checklist with scope, acceptance criteria and four status marks, plus saved API request/response replays and auth checks before committing. The developer warns that keeping the build red for most of the migration is risky on large monoliths and recommends running the checklist per module in dependency order. Confession first: I'm rusty in Java. Years of frontend work will do that to you. And yet there I was, staring at a small legacy Spring Boot 2.x service still on Java 8. Untouched dependencies, patchy tests, and every "small change" came with a knot in my stomach. The target: Java 21, Spring Framework 6 and Spring Boot 3.3.5. I did it with Claude Code. This isn't a Java upgrade guide. It's how I let AI do the heavy lifting while keeping control firmly in my hands. I asked Claude how to do this properly. The answer was very correct and very by the book: hop through Java 11 and 17, run OpenRewrite recipes, plus five specialist reviewer agents. All sensible. All a lot. Hopping through 11 and 17 is wasted effort when you're landing on 21 anyway. Five reviewers for one small app means more time managing agents than migrating code. Claude did hold one line, and it was right: split the work by area of change , not by Java version. The real risk is Spring Boot 3 landing jakarta, Security 6 and Hibernate 6 all at once. So here's what I did instead. Less by-the-book, more by-the-human. The rule: the agent that writes the code never grades it. A subagent starts with a fresh context, so it doesn't inherit the Migrator's reasoning or its excuses. It's the colleague who wasn't in the room when you wrote that "temporary" hack. I never typed "upgrade this to Java 21." The Migrator inventoried the codebase in plan mode and proposed a checklist. Once I approved it, it went into MIGRATION.md . Problem: I'm rusty, remember? I couldn't fully judge the plan. Is WebSecurityConfigurerAdapter really gone, or did Claude just not like it? So I opened a fresh Claude session and yes, asked Gemini too and grilled them on Spring Boot 3 breaking changes. AI fact-checking AI, for a language I used to know. It works, because a fresh session has no stake in the plan being right. The order: WebSecurityConfigurerAdapter to SecurityFilterChain Each item gets a scope, acceptance criteria and a "done when": Item 3: javax to jakarta Scope: src/ Java, properties, XML Acceptance: - No javax. imports, except JDK packages javax.crypto, javax.sql, ... - javax.persistence property keys migrated too - No logic changes Done when: no compile errors or removal warnings in scope, except errors owned by later items springdoc: 4, Security: 5, Hibernate types: 6, web: 7 Status: And four marks, because "done", "verified" and "approved" are different claims: to do, or sent back x Migrator says it's done v Verifier passed it, waiting for me a I approved it Before item 1, I tagged the Java 8 code pre-migration and saved some real API requests and responses. Then every item went through this: If a re-check finds an earlier item broke, that item goes back to and needs my go again. Before calling it done: one final Verifier run over everything since pre-migration , a green build with all tests passing, a replay of the saved API calls, and protected endpoints hit with and without auth. Only then did anything go into git. In my run, the build was red for most of the migration. Spring Boot 3 changes so much at once that nothing compiles until everything is done. On my small app, I judged only the end result. Don't copy that bit. On a big monolith it's walking blind through a forest: hundreds of errors, and no idea which ones the last change caused. The rule I'd teach instead: the build may be red, but an item isn't done while its files have compile errors or removal warnings that it, or an earlier item, owns.