DEV Community

Machine coding Master
Machine coding Master

Posted on

Stop Letting Claude Code Hallucinate Your Modernization: Write OpenRewrite LST Recipes Instead

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 .java files 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.JavaVisitor targeting 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:run in 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);
            }
        };
    }
}
Enter fullscreen mode Exit fullscreen mode

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)