Wat is OpenRewrite en waarom is het relevant voor jouw organisatie?

OpenRewrite en zelf recepten maken
In: Blog
Wat is OpenRewrite?
Organisaties met grote, op maat gemaakte Java-applicaties staan regelmatig voor de uitdaging om hun codebase up-to-date, veilig en onderhoudbaar te houden. OpenRewrite is een zeer krachtige tool om snel heel veel aanpassingen uit te voeren op een gecontroleerde manier. Voor maatwerkapplicaties is het beheren van technical debt en het doorvoeren van grootschalige codewijzigingen al snel complex. Hier biedt OpenRewrite uitkomst.
OpenRewrite is een open-source raamwerk dat het mogelijk maakt om broncode geautomatiseerd aan te passen op basis van vooraf gedefinieerde regels, zogenaamde ‘recepten’. Deze recepten kunnen individuele aanpassingen uitvoeren, of bestaan uit een reeks sub-recepten die samen grotere refactorings mogelijk maken. OpenRewrite is de gratis variant van het bedrijf Moderne, dat ook een betaalde cloud versie aanbiedt.
De technologie wordt ondersteund door een actieve kerngroep en vele vrijwilligers. Hierdoor is er een brede ondersteuning voor meerdere talen en frameworks zoals Java, C#, XML, SQL, JSON, Spring, Hibernate, Cucumber, enzovoorts. Zelfs op framework niveau zijn er al diverse recepten beschikbaar voor migraties, veiligheidsverbeteringen en correcties.
Waarom is dit relevant voor grote organisaties?
OpenRewrite biedt directe voordelen voor bedrijven die:
- Werken met omvangrijke Java-codebases.
- Regelmatig migraties moeten uitvoeren (bijv. naar nieuwe versies van Spring of Hibernate).
- Codekwaliteit en security centraal willen beheren.
- DevOps en CI/CD omgevingen willen integreren met automatische refactorings.
Zelfs met de vele bestaande recepten, kan het voorkomen dat je specifieke wensen hebt waarvoor je zelf recepten moet schrijven.
OpenRewrite effectief inzetten in enterprise omgevingen
De kracht van OpenRewrite komt pas echt tot zijn recht in enterprise omgevingen met complexe, multi-module Java-projecten. Toch vereist het effectief inzetten van deze toolset enige voorbereiding en afstemming met je ontwikkelproces.
Gebruik in de praktijk
OpenRewrite maakt gebruik van tooling zoals de Maven of Gradle plugin, en kan lokaal of in een CI/CD-omgeving worden gebruikt. Het gebruik van de Maven Daemon (mvnd) kan performance verbeteren. Let op: bij multi-module projecten heeft OpenRewrite veel geheugen nodig, omdat de broncode in het geheugen wordt geladen als een Lossless Semantic Tree (LST).
Om dit beheersbaar te houden:
- Zet
runPerSubmoduleaan om recepten per module te draaien. - Gebruik
rewrite:runNoForkvoor beter geheugengebruik. - Zet
skipMavenParsingaan om POM-validatie uit te schakelen.
Alle relevante afhankelijkheden moeten aanwezig zijn tijdens parsing naar een LST. Bouw het project eerst dus volledig (bijv. met -DskipTests) voor een OpenRewrite run.
Aanpak voor gebruik
- Voorbereiding
- Optimaliseer de code voor betere verwerking.
- Voeg indien nodig tijdelijke constructies toe (bijv. marker interfaces).
- Filtering
- Voorkom onnodig scannen door alleen relevante bestanden te targeten.
- Volgorde van recepten
- Let op geheugenverbruik; recepten draaien pas bij afronding.
- Voer soms meerdere runs uit bij conflicten.
- Styling
- Gebruik stylingconfiguratie om code consistent op te maken binnen CI/CD.
- Post-processing
- Verwijder tijdelijke constructies en los technical debt op.
Receptstructuur
- Laag niveau: Java-classes die daadwerkelijke code aanpassingen uitvoeren.
- Hoog niveau: YAML-bestanden met een hiërarchische structuur voor sturing van de Java-classes.
Eigen OpenRewrite-recepten maken
Hoewel OpenRewrite veel kant-en-klare recepten biedt, zijn er situaties waarin maatwerk onvermijdelijk is. Denk aan domeinspecifieke conventies, legacy-aanpassingen of bestaande recepten die bepaalde edge-cases niet ondervangen. In die gevallen loont het om eigen recepten te ontwikkelen.
Startpunt: evaluatie
- Is er een bestaand recept dat werkt?
- Kan het eenvoudig worden aangepast?
- Wat is de verhouding tussen ontwikkeltijd van een recept en de tijdwinst bij het toepassen?
Soorten recepten
- Recipe: standaard autonoom recept.
- ScanningRecipe: slaat context op in een Accumulator.
Belangrijke componenten
- Documentatie: beschrijf wat het recept doet.
- Configuratie: parameters en context, intern JSON-gebaseerd.
- Implementatie: transformatie van LST met JavaIsoVisitor.
Visitor flow
- Controleer LST-fragment.
- Maak nieuw fragment indien gewijzigd.
- Gebruik JavaTemplates om nieuwe LST-fragmenten te maken.
Voorbeeld JavaTemplate
JavaTemplate.builder(template)
.javaParser(JavaParserFactory.create(context))
.build()
.apply(getCursor(), getCoordinates().replace());
Gebruik van print en type-herstel
String template = annotation.print(getCursor());
String className = annotation.getType().print(getCursor());
fragment.withType(JavaType.SimpleType.build(fullClassName));
Praktische tips
- Combinatie van recepten in een eigen recept is mogelijk.
- Combineer LST-fragmenten met bestaande types:
newFragment.withType(oldFragment.getType()) - Gebruik
getCoordinates().replace()om contextuele vervangingen aan te geven.
Debugging en hulpmiddelen
FindMissingTypesVisitor: toont ontbrekende types (niet geschikt voor productie).
@Override
public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations multiVariable, ExecutionContext executionContext) {
doAfterVisit(new FindMissingTypes().getVisitor());
return super.visitVariableDeclarations(multiVariable, executionContext);
}
Eigen FindClassesVisitor met regex:
public class FindClassesVistor extends JavaIsoVisitor<ExecutionContext> {
final String fullyQualifiedTypeName;
final Pattern fullyQualifiedType;
public FindClassesVistor(@NonNull String fullyQualifiedTypeName) {
this.fullyQualifiedTypeName = fullyQualifiedTypeName;
fullyQualifiedType = Pattern.compile(StringUtils.aspectjNameToPattern(fullyQualifiedTypeName));
}
@Override
public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration cd, ExecutionContext ctx) {
if (cd.getType().isAssignableFrom(fullyQualifiedType)) {
return SearchResult.found(cd);
}
return super.visitClassDeclaration(cd, ctx);
}
}
Eigen OpenRewrite-recepten testen en debuggen
De API van OpenRewrite is zeer uitgebreid. Begin klein en breidt de kennis verder uit met OpenRewrite documentatie en open-source broncode.
Voor het schrijven van eigen recepten is test-driven designstrategie zeer goed toepasbaar.
Voorbeeld implementatie RewriteTest
public class SimpleTest implements RewriteTest {
public static final JavaParser.Builder<?, ?> PARSER = JavaParserFactory.create();
@BeforeAll
public static void setup() {
PARSER.typeCache(new JavaTypeCache());
}
@Test
public void simple() {
rewriteRun(spec -> spec.parser(PARSER)
.recipe(new AutoFormat()),
java("""
public class A {}
"""));
}
}
Voorbeeld JavaParserFactory
@UtilityClass
public class JavaParserFactory {
public static JavaParser.Builder<? extends JavaParser, ?> create() {
return JavaParser.fromJavaVersion()
.classpathFromResources(
new InMemoryExecutionContext(),
"lombok",
"javax.inject");
}
}
Voorbeeld dummy klasse
java("""
package a;
public class SomeClass {}
""", SourceSpec::skip)
Wanneer bestaande recepten niet voldoen
- Schrijf een aparte test als uitgangspunt voor je eigen variant.
- Let op voor reguliere expressies bij grote codefragmenten of commentaar; dit kan leiden tot memory issues.
- Recepten hebben voorzieningen om voorwaarden qua tijd te stellen.
Conclusie
Zelf recepten ontwikkelen vraagt technische diepgang, maar levert maatwerkoplossingen op die goed aansluiten bij jouw codebase en ontwikkelproces. Het is een iteratief proces van ontwerpen, testen en toepassen dat uiteindelijk leidt tot schaalbare en herbruikbare transformaties.
Nieuwsgierig of maatwerk voor jou het verschil kan maken?
Neem contact met ons op voor maatwerkoplossingen in Java, geleverd door een ervaren team dat stabiliteit, continuïteit en optimalisatie garandeert voor jouw bedrijfsprojecten.