Von Groovy zu Kotlin DSL: Gradle Build Scripts migrieren
Von Groovy zu Kotlin DSL: Gradle Build Scripts migrieren
Wir nutzen Gradle seit 2018 als Build-Tool für unsere JVM-Projekte -- angefangen mit dem Wechsel von Maven zu Gradle, später vertieft in unserem Artikel über Groovy als DSL-Sprache in Gradle. Parallel dazu hat sich Kotlin über die Jahre zu unserer bevorzugten Sprache für Spring-Boot-Backends entwickelt, wie wir in unseren Beiträgen zu Kotlin im Spring-Ökosystem und dem Praxis-Erfahrungsbericht beschrieben haben. Trotzdem haben wir unsere Build-Skripte lange in Groovy belassen. Das haben wir jetzt geändert.
Warum überhaupt wechseln?
Groovy in Gradle funktioniert. Das haben wir über Jahre hinweg bewiesen. Aber es gibt ein Problem, das mit wachsenden Projekten immer deutlicher wird: Die IDE weiss nicht, was in einer build.gradle passiert. Groovys dynamische Typisierung, die wir in unserem Groovy-Artikel noch als Stärke beschrieben haben, wird beim Editieren von Build-Skripten zur Schwäche. Auto-Completion funktioniert bestenfalls rudimentär, Refactoring-Support ist praktisch nicht vorhanden, und Fehler fallen erst zur Laufzeit auf -- also beim nächsten gradle build.
Die Kotlin DSL löst genau dieses Problem. Build-Skripte sind statisch typisiert, IntelliJ IDEA kennt jeden Typ, jede Methode, jede Property. Man bekommt echte Auto-Completion, kann per Cmd+Click in die Gradle-API navigieren und sieht Fehler sofort als rote Unterstreichungen. Für Teams, die ohnehin Kotlin schreiben, gibt es noch einen weiteren Vorteil: Man hat eine einheitliche Sprache für Produktivcode und Build-Logik.
Mit Gradle 7.x ist die Kotlin DSL ausgereift. Die Performance-Probleme früherer Versionen sind behoben, die IDE-Integration in IntelliJ IDEA funktioniert zuverlässig, und die gesamte Gradle-Dokumentation liefert Beispiele in beiden Varianten. Es gibt keinen guten Grund mehr, bei Groovy zu bleiben, wenn man Kotlin-Projekte baut.
Die Migrationsstrategie
Gradle erlaubt es, Groovy- und Kotlin-DSL-Skripte innerhalb eines Builds zu mischen. Das ist der Schlüssel für eine risikoarme Migration. Man muss nicht alles auf einmal umstellen, sondern kann Datei für Datei vorgehen.
Unsere Reihenfolge:
settings.gradlezusettings.gradle.ktsmigrieren- Das Root-
build.gradlezubuild.gradle.ktsumstellen - Subproject-Build-Skripte einzeln migrieren
Der Schritt ist jeweils derselbe: Datei umbenennen, Syntax anpassen, Build ausführen, testen. Durch die Mischbarkeit kann man nach jedem Schritt deployen, ohne dass etwas kaputt geht.
Die wichtigsten Syntaxunterschiede
Strings: Doppelte statt einfache Anführungszeichen
In Groovy sind einfache und doppelte Anführungszeichen für Strings erlaubt. In Kotlin gibt es nur doppelte. Das betrifft praktisch jede Zeile im Build-Skript.
Groovy:
group = 'com.encircle360'
version = '1.0.0-SNAPSHOT'
Kotlin DSL:
group = "com.encircle360"
version = "1.0.0-SNAPSHOT"
Plugins Block
Der Plugins-Block ändert sich syntaktisch, weil Kotlin Klammern bei Methodenaufrufen verlangt und keine impliziten Methodenaufrufe ohne Klammern erlaubt.
Groovy:
plugins {
id 'java'
id 'org.springframework.boot' version '2.7.0'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'org.jetbrains.kotlin.jvm' version '1.6.21'
id 'org.jetbrains.kotlin.plugin.spring' version '1.6.21'
}
Kotlin DSL:
plugins {
java
id("org.springframework.boot") version "2.7.0"
id("io.spring.dependency-management") version "1.0.11.RELEASE"
id("org.jetbrains.kotlin.jvm") version "1.6.21"
id("org.jetbrains.kotlin.plugin.spring") version "1.6.21"
}
Für Standard-Plugins wie java gibt es in der Kotlin DSL Accessor-Properties, die man direkt ohne id() verwenden kann. Das ist kürzer und typsicher.
Zuweisungen mit dem =-Operator
In Groovy kann man Properties mit einem Leerzeichen zuweisen -- das ist eigentlich ein Methodenaufruf. In Kotlin muss der Zuweisungsoperator = verwendet werden.
Groovy:
sourceCompatibility = '17'
targetCompatibility = '17'
springBoot {
mainClass = 'com.encircle360.app.Application'
}
Kotlin DSL:
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
springBoot {
mainClass.set("com.encircle360.app.Application")
}
Hier zeigt sich ein weiterer Vorteil der Kotlin DSL: Die Zuweisung von sourceCompatibility nutzt den Enum JavaVersion statt eines Strings. Fehler wie sourceCompatibility = '1.17' fallen sofort auf.
Dependencies
Bei Dependencies werden Klammern und doppelte Anführungszeichen Pflicht. Ausserdem fällt das in Groovy mögliche Weglassen der Klammern bei Methodenaufrufen weg.
Groovy:
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'com.fasterxml.jackson.module:jackson-module-kotlin'
implementation 'org.jetbrains.kotlin:kotlin-reflect'
runtimeOnly 'org.postgresql:postgresql'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
Kotlin DSL:
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.jetbrains.kotlin:kotlin-reflect")
runtimeOnly("org.postgresql:postgresql")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}
Der Unterschied wirkt kosmetisch, hat aber einen handfesten Nutzen: IntelliJ erkennt die Dependency-Koordinaten als Strings in einem Methodenaufruf und kann darauf Code-Inspektionen anwenden.
Custom Tasks
Task-Definitionen ändern sich am deutlichsten. Die Groovy-Syntax mit task taskName(type: TaskType) gibt es nicht mehr. Stattdessen nutzt man tasks.register<TaskType>("taskName").
Groovy:
task cleanBuildDir(type: Delete) {
delete 'build/generated'
}
task printVersion {
doLast {
println "Version: ${project.version}"
}
}
Kotlin DSL:
tasks.register<Delete>("cleanBuildDir") {
delete("build/generated")
}
tasks.register("printVersion") {
doLast {
println("Version: ${project.version}")
}
}
Die generische Typangabe <Delete> macht sofort klar, welcher Task-Typ verwendet wird. In der IDE bekommt man dadurch Auto-Completion für alle Properties und Methoden des jeweiligen Task-Typs.
Extra Properties statt ext
In Groovy nutzt man häufig den ext-Block, um projektweite Variablen zu definieren. Die Kotlin DSL bietet dafür den extra-Mechanismus.
Groovy:
ext {
springCloudVersion = '2021.0.3'
kotlinVersion = '1.6.21'
}
Kotlin DSL:
val springCloudVersion by extra("2021.0.3")
val kotlinVersion by extra("1.6.21")
In Subprojects greift man dann mit val springCloudVersion: String by rootProject.extra darauf zu. Das ist etwas ausführlicher als in Groovy, dafür aber typsicher -- die Variable hat einen konkreten Typ, und der Compiler prüft das.
Stolpersteine in der Praxis
Buildscript-Block: Wer noch einen buildscript-Block für ältere Plugin-Repositories nutzt, muss die gleichen Syntaxänderungen auch dort durchführen. Besser ist es, den buildscript-Block bei der Gelegenheit gleich durch den plugins-Block mit pluginManagement in der settings.gradle.kts zu ersetzen.
Groovy-Plugins: Einige ältere Gradle-Plugins liefern ihre Konfiguration nur als Groovy-Extensions aus. In seltenen Fällen funktioniert die Konfiguration in der Kotlin DSL nicht direkt, und man muss auf withGroovyBuilder oder the<ExtensionType>() ausweichen. Bei aktuellen Plugins wie Spring Boot, Kotlin oder Flyway hatten wir keine Probleme.
String-Interpolation: Groovy nutzt ${variable} nur in doppelten Anführungszeichen, in einfachen werden Variablen nicht aufgelöst. Da Kotlin generell doppelte Anführungszeichen nutzt, funktioniert $variable und ${expression} immer. Aber aufpassen: Wenn man $ als literales Zeichen braucht, muss man es escapen.
Performance beim ersten Build: Nach dem Umbenennen zu .gradle.kts dauert der erste Build etwas länger, weil Gradle das Skript erstmalig kompiliert. Danach greift der Script-Cache, und die Build-Zeiten sind vergleichbar mit Groovy.
Unser Fazit
Wir haben die Migration in einem bestehenden Multi-Module-Projekt mit sechs Subprojects durchgeführt. Insgesamt hat die Umstellung etwa einen halben Tag gedauert, verteilt auf mehrere Merge Requests. Der grösste Zeitaufwand steckt nicht in der Syntax-Umstellung selbst -- die ist mechanisch -- sondern im Testen, ob alle Build-Varianten, Profile und CI-Pipelines weiterhin funktionieren.
Das Ergebnis hat sich gelohnt. Die IDE-Unterstützung ist spürbar besser, Fehler in Build-Skripten fallen früher auf, und neue Teammitglieder, die Kotlin kennen, finden sich sofort zurecht. Für Projekte, die ohnehin Kotlin als Entwicklungssprache nutzen, ist die Migration eine klare Empfehlung. Der Aufwand ist überschaubar, der Nutzen im Alltag dauerhaft spürbar.
Geschrieben von
Patrick HütterGründer & Software-Architekt
Softwarearchitekt, Ingenieur und Unternehmer. Patrick baut seit über einem Jahrzehnt Produkte und Plattformen — von Enterprise-Backends über Cloud-Native-Infrastruktur bis hin zu KI-gestützten Anwendungen. Als Gründer von encircle360 verbindet er technische Tiefe mit unternehmerischem Denken und treibt Open-Source-Projekte voran, die echten Impact haben.
Das könnte dich auch interessieren
Vom Manifest zur Produktion: Wie ADL, A2A und das Inference Gateway die Agenten-Infrastruktur revolutionieren
05.07.2026 · 12 Min. Lesezeit
Agent-Orchestrierung mit Java: Wie Sie LLM-Agenten produktionsreif in die JVM bringen
04.07.2026 · 6 Min. Lesezeit
Spring AI: Wie Java-Entwickler endlich KI-Features ohne Umwege integrieren
25.03.2026 · 5 Min. Lesezeit