Stop Letting Claude Code Hallucinate Your Modernization: Write OpenRewrite LST Recipes Instead
Letting autonomous AI agents directly rewrite million-line Java monoliths is a dangerous game of Russian roulette with silent runtime bugs. The real way to execute large-scale JDK 26 upgrades is using Claude Code CLI to author deterministic OpenRewrite LST recipes—not blindly edit source files.
Want to go deeper? javalld.com — machine coding interview problems with working Java code and full execution traces.
Why Most Developers Get This Wrong
- Yolo-prompting Claude Code CLI to edit hundreds of
.javafiles directly, introducing non-existent API calls and broken imports. - Treating OpenRewrite Lossless Semantic Trees (LST) like naive regex text manipulation instead of type-attributed AST structures.
- Relying on LLM self-correction loops to fix hallucinated method signatures instead of relying on the Java compiler.
The Right Way
Use Claude Code CLI to generate, unit-test, and refine type-safe OpenRewrite Java recipes, then let rewrite-maven-plugin execute them deterministically.
- Prompt Claude Code to extend
org.openrewrite.java.JavaVisitortargeting specific legacy patterns (e.g., Spring Boot 3+ or Jakarta EE migrations). - Validate recipe execution against OpenRewrite's type-attribution engine to guarantee zero semantic drift across your codebase.
- Execute the generated recipe via
mvn rewrite:runin a clean CI container for 100% reproducible diffs. - Isolate LLM non-determinism to the recipe authoring phase—keep your production target repo completely deterministic.
Show Me The Code (or Example)
Prompt Claude Code CLI to generate this exact imperatively-safe recipe structure:
public class MigrateToJDK26VirtualThreads extends Recipe {
@Override
public String getDisplayName() { return "Migrate Executors to Virtual Threads"; }
@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return new JavaIsoVisitor<ExecutionContext>() {
private final MethodMatcher MATCHER = new MethodMatcher("java.util.concurrent.Executors newFixedThreadPool(int)");
@Override
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) {
if (MATCHER.matches(method)) {
return JavaTemplate.builder("Executors.newVirtualThreadPerTaskExecutor()")
.build().apply(getCursor(), method.getCoordinates().replace());
}
return super.visitMethodInvocation(method, ctx);
}
};
}
}
Key Takeaways
- LLMs generate logic; deterministic AST engines transform code—never confuse the two responsibilities.
- Generating OpenRewrite recipes via Claude Code gives you compiler-backed type safety across enterprise-scale Java migrations.
- If an AI-driven refactor isn't backed by a Lossless Semantic Tree parser, it belongs nowhere near your production monolith.
Top comments (0)