OpenRewrite recipes: deterministic, type-aware code change at scale

scroll↓
What a recipe is↓

A recipe is a program shaped to the problem.

A recipe is a deterministic, composable program. It walks a Lossless Semantic Tree of the code, recognizes an exact target by its resolved type (not just its text) and rewrites it precisely. The business problem is a lock and the recipe is the key cut to fit it.

Recipes compose. The Spring Boot 4 upgrade is over 4,000 recipes all composed together, with hundreds of preconditions deciding where each step applies.

The Moderne recipe builder with the Spring Boot 4 recipe open: a graph of several thousand connected recipe nodes on the left, and on the right a recipe list of 4,340 steps with 427 preconditions, headed by Migrate to Spring Boot 4.0, ready to dry run on 45,877 repositories.

A key already exists for many of the thorniest business problems in code.

The same lock repeats across an industry. So the catalog is broad on purpose. Thousands of small, sharp keys compose into composites, each fanning out into tens of thousands of sub-recipes in a single run.

10,000+ deterministic, type-aware recipes, spanning Java, Kotlin, C#, Go, Python and JavaScript, across 340+ frameworks and tools.

As it turns out, agents like writing recipes
rather than doing manual edits too.

Ask an engineer to do something 3 times and they’ll automate it! As it turns out, agents write scripts for just about everything too. They love writing new recipes. Filling out the OpenRewrite recipe marketplace used to be a manual lift of a whole community.

When an agent writes a recipe and its unit tests and then tests it, it’s like setting a ratchet strap. With every additional edge case that gets discovered, added, and tested, the ratchet strap tightens. So the next time the agent is reaching for that change, it’s already got a tightened down highly calibrated tool to get the job done.

Agents also know that applying a recipe to a codebase means that it won’t miss any occurrences.

UseAppendLine.kt
val UseAppendLine: Recipe = recipe(
    displayName = "Use `appendLine()` instead of `appendln()`",
    description = "`Appendable.appendln()` was deprecated in Kotlin 1.4 in favor of `appendLine()` (consistent naming with `Reader.readLine()`).",
) {
    edit {
        rewrite { sb: StringBuilder -> sb.appendln() } to { sb -> sb.appendLine() }
    }
}

val UseAppendLineWithValue: Recipe = recipe(
    displayName = "Use `appendLine(value)` instead of `appendln(value)`",
    description = "`Appendable.appendln(value)` was deprecated in Kotlin 1.4 in favor of `appendLine(value)`.",
) {
    edit {
        rewrite { sb: StringBuilder, v: String -> sb.appendln(v) } to { sb, v -> sb.appendLine(v) }
    }
}

val UseAppendLineChar: Recipe = recipe(
    displayName = "Use `appendLine(char)` instead of `appendln(char)`",
    description = "`Appendable.appendln(value: Char)` was deprecated in Kotlin 1.4 in favor of `appendLine(value: Char)`.",
) {
    edit {
        rewrite { sb: StringBuilder, c: Char -> sb.appendln(c) } to { sb, c -> sb.appendLine(c) }
    }
}

A diff your engineers can actually trust.

Just like engineers like writing automations for repetitive tasks, we also tend to trust the outcome of an automation more than something done by hand.

Just ask any engineer that was around before immutable infrastructure patterns!

Frequently asked questions

A recipe is a deterministic program that traverses your code’s Lossless Semantic Tree (LST) using the visitor pattern: visiting each node, recognizing an exact target by its resolved type, and editing it precisely. The same input always produces the same result, with no AI hallucination in execution.

An agent editing source is a fresh guess on every run. It’s text-level, non-deterministic, and blind to type information across files. A recipe is authored once, validated, and then applies an identical, type-aware change every run, across any number of repositories. The agent’s job is to reach for (or write) the recipe, not to be the editor.

The open catalog holds 10,000+ recipes spanning Java, Kotlin, C#, Go, Python and JavaScript, across more than 340 frameworks and tools: Spring, Quarkus, Hibernate, JUnit, AssertJ, the major clouds, CVE patches and more.

Yes. Recipes are authored three ways: declarative YAML that composes existing recipes into a named pipeline (no code), Refaster-style before/after templates the compiler verifies, or imperative Java visitors for full control over the LST.

Recipes produce format-preserving, minimal diffs. Imports, comments and whitespace they didn’t touch are unchanged, and the result is deterministic and identical every run. Engineers review a clean, trustworthy diff instead of a noisy, approximate one.