The following list covers all custom recipes provided by allwrite.
The allwrite-spi module provides the following public helpers for recipe authors:
| Helper | Purpose |
|---|---|
AllwriteRecipe |
Base class for regular recipes. Builds display metadata and allwrite tags. |
AllwriteScanningRecipe |
Base class for OpenRewrite scanning recipes with the same metadata support. |
RecipeMetadata |
Builds display name, description, visibility, friendly-name, version, and Dependabot tags. |
RecipeVisibility |
Marks a recipe as INTERNAL or PUBLIC. |
ParsingAwareRecipe |
Restricts the files parsed for a recipe. |
ClasspathAwareRecipe |
Requests additional parser classpath entries and isolated execution. |
PostprocessingRecipe |
Runs additional work after recipe changes are applied. |
PostprocessingResult |
Reports postprocessing success or a failure with an error message. |
See Writing recipes for usage guidelines and examples.
pl.allegro.tech.allwrite.recipes.yaml.ExpandMappings { data-toc-label=”ExpandMappings” }Transforms flat/collapsed YAML properties into a hierarchical structure and merges duplicate paths.
Options:
| Name | Type | Required | Description |
|---|---|---|---|
prefix |
String |
No | Only transform entries matching this prefix. Must match key parts exactly (e.g. myapp will not match myapplication). |
excludes |
List<String> |
No | Do not transform entries matching these prefixes. |
Before:
myapp.metrics.graphite.enabled: true
myapp.metrics.graphite:
host: localhost
port: 2003
myapp:
i18n:
enabled: true
After:
myapp:
metrics:
graphite:
enabled: true
host: localhost
port: 2003
i18n:
enabled: true
pl.allegro.tech.allwrite.recipes.yaml.UnnestProperties { data-toc-label=”UnnestProperties” }Removes one level of nesting from a YAML mapping at a specified path, moving child entries up to the parent level.
Options:
| Name | Type | Required | Description |
|---|---|---|---|
targetPath |
String |
Yes | Dot-separated path to the mapping entry to unnest. |
Before:
spring:
groovy:
template:
configuration:
auto-indent: true
auto-new-line: true
After (with targetPath = spring.groovy.template.configuration):
spring:
groovy:
template:
auto-indent: true
auto-new-line: true
pl.allegro.tech.allwrite.recipes.yaml.AddTopLevelLineBreaks { data-toc-label=”AddTopLevelLineBreaks” }Ensures top-level YAML mapping entries are separated by blank lines for conventional formatting.
Before:
server:
port: 8080
spring:
application:
name: my-app
management:
endpoints:
enabled: true
After:
server:
port: 8080
spring:
application:
name: my-app
management:
endpoints:
enabled: true
pl.allegro.tech.allwrite.recipes.yaml.DeleteProperty { data-toc-label=”DeleteProperty” }Enhanced version of OpenRewrite’s DeleteProperty that:
Options:
| Name | Type | Required | Description |
|---|---|---|---|
propertyKey |
String |
Yes | The key to be deleted. |
coalesce |
Boolean |
No | Simplify nested map hierarchies into their simplest dot separated property form. |
relaxedBinding |
Boolean |
No | Whether to match the propertyKey using relaxed binding rules. Defaults to true. If you want to use exact matching in your search, set this to false. |
filePattern |
String |
No | A glob expression representing a file path to search for (relative to the project root). Blank/null matches all. |
Before:
server.port: 8080 # comment should stay
management.server.port: 8081 # comment should disappear
smth-else: 1
After (with propertyKey = management.server.port):
server.port: 8080 # comment should stay
smth-else: 1
pl.allegro.tech.allwrite.recipes.yaml.FindKey { data-toc-label=”FindKey” }A lighter and faster alternative to OpenRewrite’s FindKey. Searches for a YAML key using simple case-insensitive matching instead of JsonPath. Intended for use as a precondition that fires when a YAML document contains a specified key.
Options:
| Name | Type | Required | Description |
|---|---|---|---|
key |
String |
No | The YAML key to search for (case-insensitive, dot-separated path). |
pl.allegro.tech.allwrite.recipes.yaml.YamlEntryHasValue { data-toc-label=”YamlEntryHasValue” }Precondition recipe that matches YAML entries at a given JSON path having a specific value.
Options:
| Name | Type | Required | Description |
|---|---|---|---|
key |
String |
Yes | JSON path expression to locate the entry. |
expectedValue |
String |
Yes | The value the entry must have to match. |
pl.allegro.tech.allwrite.recipes.gradle.AddGradleDependency { data-toc-label=”AddGradleDependency” }A two-pass scanning recipe that adds a dependency to a Gradle project. In the scan phase, it parses the TOML version catalog (gradle/libs.versions.toml) and discovers module roots. In the transform phase, it adds the library entry to the version catalog and a dependency reference in build.gradle(.kts).
Options:
| Name | Type | Required | Description |
|---|---|---|---|
configuration |
String |
Yes | Gradle configuration (e.g. implementation, testImplementation). |
groupId |
String |
Yes | Dependency group ID. |
artifactId |
String |
Yes | Dependency artifact ID. |
version |
String |
No | Dependency version. Used when no version catalog is present. |
versionCatalogName |
String |
No | Alias for the dependency in the version catalog. Auto-generated from coordinates if not specified. |
Before (with configuration = "testRuntimeOnly", groupId = "org.junit.platform", artifactId = "junit-platform-launcher"):
gradle/libs.versions.toml:
[libraries]
mylib-starter = { group = "com.example.lib", name = "mylib-starter" }
build.gradle.kts:
dependencies {
implementation(libs.test)
}
After:
gradle/libs.versions.toml:
[libraries]
mylib-starter = { group = "com.example.lib", name = "mylib-starter" }
junit-platform-launcher = { group = "org.junit.platform", name = "junit-platform-launcher" }
build.gradle.kts:
dependencies {
implementation(libs.test)
testRuntimeOnly(libs.junit.platform.launcher)
}
pl.allegro.tech.allwrite.recipes.gradle.ChangeGradleDependency { data-toc-label=”ChangeGradleDependency” }Changes a Gradle dependency’s group ID, artifact ID, and optional version. It supports build.gradle, build.gradle.kts, and gradle/libs.versions.toml, updating matching version-catalog entries and refs when present. The old group and artifact IDs accept glob patterns, so * can match any coordinate segment.
Options:
| Name | Type | Required | Description |
|---|---|---|---|
oldGroupId |
String |
Yes | Dependency group ID to replace. Accepts glob patterns. |
oldArtifactId |
String |
Yes | Dependency artifact ID to replace. Accepts glob patterns. |
newGroupId |
String |
No | Replacement group ID. Defaults to the old group ID. |
newArtifactId |
String |
No | Replacement artifact ID. Defaults to the old artifact ID. |
newVersion |
String |
No | Replacement version. If omitted, the version is removed. |
Before (with oldGroupId = "com.fasterxml.jackson.module", oldArtifactId = "jackson-module-afterburner"):
build.gradle.kts:
dependencies {
implementation("com.fasterxml.jackson.module:jackson-module-afterburner:2.17.2")
}
After:
build.gradle.kts:
dependencies {
implementation("tools.jackson.module:jackson-module-blackbird:3.1.4")
}
pl.allegro.tech.allwrite.recipes.gradle.UpdateGradleDependency { data-toc-label=”UpdateGradleDependency” }Updates dependency versions using regular expressions. Converts build files to plain text and applies regex-based replacements. Supports dependency declarations in multiple formats:
classpath("GROUP:ID:1.0.0")classpath("GROUP", "ID", "1.0.0")classpath group: 'GROUP', name: 'ID', version: '1.0.0'classpath(group = "GROUP", name = "ID", version = "1.0.0")Options:
| Name | Type | Required | Description |
|---|---|---|---|
groupId |
String |
Yes | Dependency group ID to match. |
artifactId |
String |
Yes | Dependency artifact ID to match. |
targetVersion |
String |
Yes | The new version to set. |
sourceVersionPattern |
String |
No | Regex pattern for the current version. Defaults to \d+.\d+.\d+. |
filePatterns |
List<String> |
No | Glob patterns for files to scan. Defaults to *.gradle, *.gradle.kts, and gradle/*.toml. |
Before (with groupId = "com.example", artifactId = "some-dependency", targetVersion = "2.0.0"):
classpath("com.example:some-dependency:1.0.0")
After:
classpath("com.example:some-dependency:2.0.0")
pl.allegro.tech.allwrite.recipes.java.ChangeType { data-toc-label=”ChangeType” }Enhanced version of OpenRewrite’s ChangeType with additional support for renaming variables.
Options:
| Name | Type | Required | Description |
|---|---|---|---|
oldFullyQualifiedTypeName |
String |
Yes | Fully-qualified class name of the original type. |
newFullyQualifiedTypeName |
String |
Yes | Fully-qualified class name of the replacement type, or the name of a primitive such as “int”. The OuterClassName$NestedClassName naming convention should be used for nested classes. |
ignoreDefinition |
Boolean |
No | When set to true the definition of the old type will be left untouched. This is useful when you’re replacing usage of a class but don’t want to rename it. |
Before:
public class OldType {}
public class Main {
static void main() {
OldType oldType = new OldType();
}
}
After:
public class NewType {}
public class Main {
static void main() {
// the vanilla OpenRewrite would keep 'oldType' variable name
NewType newType = new NewType();
}
}
pl.allegro.tech.allwrite.recipes.java.ChangeRecordField { data-toc-label=”ChangeRecordField” }Renames a field on a Java record type across all usages, including field access expressions and accessor method invocations.
Options:
| Name | Type | Required | Description |
|---|---|---|---|
declaringTypeFqn |
String |
Yes | Fully qualified name of the record type. |
oldFieldName |
String |
Yes | Current field name. |
newFieldName |
String |
Yes | New field name. |
Given third-party class:
public record SomeRecord(String oldName) { }
then has been changed to:
public record SomeRecord(String newName) { }
Before (with declaringTypeFqn = "com.example.SomeRecord", oldFieldName = "oldName", newFieldName = "newName"):
class Foo {
String foo(SomeRecord someRecord) {
return someRecord.oldName();
}
}
After:
class Foo {
String foo(SomeRecord someRecord) {
return someRecord.newName();
}
}
Before (with declaringTypeFqn = "com.example.SomeRecord", oldFieldName = "oldName", newFieldName = "newName"):
fun foo(someRecord: SomeRecord): String {
return someRecord.oldName
}
After:
fun foo(someRecord: SomeRecord): String {
return someRecord.newName
}
pl.allegro.tech.allwrite.recipes.java.SimplifyMethodChain { data-toc-label=”SimplifyMethodChain” }Enhanced version of OpenRewrite’s SimplifyMethodChain with support for Kotlin.
Options:
| Name | Type | Required | Description |
|---|---|---|---|
methodPatternChain |
List<String> |
Yes | A list of method patterns that are called in sequence. |
newMethodName |
String |
Yes | The method name that will replace the existing name. The new method name target is assumed to have the same arguments as the last method in the chain. |
matchOverrides |
Boolean |
No | When enabled, find methods that are overrides of the method pattern. |
Before:
fun getAuthorName(book: Book) = book.author.name
After:
fun getAuthorName(book: Book) = book.authorName
pl.allegro.tech.allwrite.recipes.java.RemoveUnusedPrivateFields { data-toc-label=”RemoveUnusedPrivateFields” }Enhanced version of OpenRewrite’s RemoveUnusedPrivateFields with additional onlyRemoveFieldsOfType parameter.
Options:
| Name | Type | Required | Description |
|---|---|---|---|
onlyRemoveFieldsOfType |
String[] |
No | Array of fully-qualified class names |
Before:
class Example {
private com.example.Foo unusedFoo;
private com.example.Bar unusedBar;
}
After (with onlyRemoveFieldsOfType = [com.example.Foo])
class Example {
private com.example.Bar unusedBar;
}
pl.allegro.tech.allwrite.recipes.java.RemoveUnusedImportsOfType { data-toc-label=”RemoveUnusedImportsOfType” }Removes unused imports, but only of given type.
Options:
| Name | Type | Required | Description |
|---|---|---|---|
types |
String[] |
No | Array of fully-qualified class names |
Before:
import com.example.Foo; // unused
import com.example.Bar; // unused
After (with types = [com.example.Foo]):
import com.example.Bar; // unused
pl.allegro.tech.allwrite.recipes.java.ReplaceFactoryWithConstructor { data-toc-label=”ReplaceFactoryWithConstructor” }Replaces factory method invocations with direct constructor calls. Handles both new Factory().create(args) and identifier-based factory invocations. Automatically manages import changes.
Options:
| Name | Type | Required | Description |
|---|---|---|---|
fullyQualifiedTypeName |
String |
Yes | Fully qualified name of the target type to construct. |
factoryClassNamePattern |
String |
Yes | Regex pattern matching the factory class name. |
Before (with fullyQualifiedTypeName = "com.example.MyClass", factoryClassNamePattern = "MyClassFactory"):
import com.example.MyClassFactory;
class Foo {
void bar() {
MyClass obj = new MyClassFactory().create(arg1, arg2);
}
}
After:
import com.example.MyClass;
class Foo {
void bar() {
MyClass obj = new MyClass(arg1, arg2);
}
}
pl.allegro.tech.allwrite.recipes.spring.FindSpringProperty { data-toc-label=”FindSpringProperty” }Searches for a Spring property by key across application*.properties, application*.yml, and application*.yaml files. Supports relaxed binding and glob patterns for both key matching and profile filtering via file name suffix.
Options:
| Name | Type | Required | Description |
|---|---|---|---|
propertyKey |
String |
Yes | The property key to search for. Compared using relaxed binding, supports glob. |
expectedValue |
String |
Yes | The property value to match. If null, matches any value. |
fileNameSuffix |
String |
No | Glob pattern for file name suffix, used to filter by Spring profile (e.g. -integration). |
pl.allegro.tech.allwrite.recipes.spring.DeleteSpringProperty { data-toc-label=”DeleteSpringProperty” }Deletes a Spring property by key from both YAML and .properties files.
Options:
| Name | Type | Required | Description |
|---|---|---|---|
propertyKey |
String |
Yes | The property key to delete. |
Before (with propertyKey = "myapp.isolated-environment"):
myapp:
isolated-environment:
nested-object:
scalar: 123
list:
- a
- b
test:
123
After:
myapp:
test:
123
pl.allegro.tech.allwrite.recipes.spring.DeleteSpringPropertyWithValue { data-toc-label=”DeleteSpringPropertyWithValue” }Deletes a Spring property only if it has a specific value. Works across both YAML and .properties files.
Options:
| Name | Type | Required | Description |
|---|---|---|---|
propertyKey |
String |
Yes | The property key to delete. |
propertyValue |
String |
Yes | The exact value the property must have to be deleted. |
Before (with propertyKey = "management.endpoint.configprops", propertyValue = "true"):
management:
endpoint:
configprops: true
health:
enabled: true
After:
management:
endpoint:
health:
enabled: true
pl.allegro.tech.allwrite.recipes.spring.DeleteSpringPropertyFromSpringAnnotations { data-toc-label=”DeleteSpringPropertyFromSpringAnnotations” }Removes property entries from @SpringBootTest(properties = ...) and @TestPropertySource(properties = ...) annotations in Java source code. Supports glob matching on property names with relaxed binding.
Options:
| Name | Type | Required | Description |
|---|---|---|---|
propertyName |
String |
Yes | The property key to remove. Supports glob (e.g. management.metrics.binders.*.enabled). |
Before (with propertyName = "myapp.test"):
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest(properties = {
"server.port=8080",
"myapp.test=1",
"myapp.best=2"
})
class Example {}
After:
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest(properties = {
"server.port=8080",
"myapp.best=2"
})
class Example {}
pl.allegro.tech.allwrite.recipes.spring.ChangeSpringPropertyKey { data-toc-label=”ChangeSpringPropertyKey” }Renames a Spring property key across YAML, .properties, and other files (e.g. Markdown). For YAML and .properties, delegates to OpenRewrite’s ChangeSpringPropertyKey. For other files, performs a regex find-and-replace supporting both lower-hyphen and lowerCamel case formats.
Options:
| Name | Type | Required | Description |
|---|---|---|---|
oldKey |
String |
Yes | The property key to rename. Supports glob. |
newKey |
String |
Yes | The new name for the property key. |
Before (with oldKey = "i18n.language-bundle.enabled", newKey = "myapp.i18n.language-bundle.enabled"):
i18n:
language-bundle:
enabled: true
After:
myapp.i18n.language-bundle.enabled: true
pl.allegro.tech.allwrite.recipes.spring.RenameTaskExecutorBean { data-toc-label=”RenameTaskExecutorBean” }Adds @Qualifier("applicationTaskExecutor") to TaskExecutor injection points (constructor parameters, @Bean method parameters, and @Autowired fields) to align with the Spring Boot 3.5 bean naming change. Skips projects that define their own custom taskExecutor bean.
Before:
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Component;
@Component
public class MyService {
public MyService(TaskExecutor taskExecutor) {}
}
After:
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Component;
@Component
public class MyService {
public MyService(@Qualifier("applicationTaskExecutor") TaskExecutor taskExecutor) {}
}
pl.allegro.tech.allwrite.recipes.spring.AddNonNullableTypeBoundsToSpringRepositories { data-toc-label=”AddNonNullableTypeBoundsToSpringRepositories” }Adds : Any upper bounds to type parameters of Kotlin classes/interfaces extending Spring Data repository interfaces,
as required by Spring Framework 7 / Spring Boot 4 JSpecify nullability annotations. Only applies to Kotlin source files.
This recipe is automatically included in the Spring Boot 4.0 migration.
Before:
import org.springframework.data.repository.CrudRepository
interface UserRepository<T, ID> : CrudRepository<T, ID>
After:
import org.springframework.data.repository.CrudRepository
interface UserRepository<T : Any, ID : Any> : CrudRepository<T, ID>
pl.allegro.tech.allwrite.recipes.spring.ReplaceStatusCodeValue { data-toc-label=”ReplaceStatusCodeValue” }Replaces deprecated ResponseEntity.getStatusCodeValue() / .statusCodeValue with getStatusCode().value() / .statusCode.value() as required by Spring
Framework 7 / Spring Boot 4. Works across Java, Groovy, and Kotlin source files. Included in the SpringBoot4_0 upgrade recipe.
Before (Java):
import org.springframework.http.ResponseEntity;
class Example {
void test() {
ResponseEntity<String> response = ResponseEntity.ok("hello");
int status = response.getStatusCodeValue();
}
}
After (Java):
import org.springframework.http.ResponseEntity;
class Example {
void test() {
ResponseEntity<String> response = ResponseEntity.ok("hello");
int status = response.getStatusCode().value();
}
}
Before (Kotlin/Groovy):
import org.springframework.http.ResponseEntity
class Example {
fun test() {
val response: ResponseEntity<String> = ResponseEntity.ok("hello")
val status: Int = response.statusCodeValue
}
}
After (Kotlin/Groovy):
import org.springframework.http.ResponseEntity
class Example {
fun test() {
val response: ResponseEntity<String> = ResponseEntity.ok("hello")
val status: Int = response.statusCode.value()
}
}