Build-Werkzeuge für Java

Maven oder Gradle: welches Build-Werkzeug euer Java-Projekt tatsächlich braucht

Die Entscheidung fällt nicht am Dateiformat, sondern an drei Fragen: Wie oft baut ihr, wie viele Module hängen zusammen, und wie viel Sonderfall steckt in eurem Ablauf.

KI-generiertDieses Bild wurde mit KI erzeugt · Yves Hoppe / KI / cmt
Seit 1997 am Markt Kleine Gruppen Präsenz und Live-Online Zertifizierte Trainer
Worum es geht

Gewechselt wird aus Geschmack, gemerkt wird es beim nächsten Wechsel im Team

Der Anlass für die Diskussion ist selten eine Messung. Meistens ist es ein neues Teammitglied, das Gradle aus dem letzten Projekt kennt, oder eine Anleitung im Netz, deren Beispiel in Kotlin geschrieben ist. Der Umbau ist dann schnell gemacht, und die ersten Wochen fühlen sich gut an. Die Rechnung kommt später: Wenn die Person, die den Build geschrieben hat, das Haus verlässt, steht ein Skript da, das niemand mehr in Gänze versteht, und die Fehlersuche darin dauert länger als die Fehlersuche im Anwendungscode.

Der umgekehrte Fehler ist genauso teuer, fällt aber langsamer auf. Ein Team bleibt bei Maven, weil es immer so war, obwohl der Build zwanzig Module umfasst und ein vollständiger Durchlauf auf dem Build-Server eine Viertelstunde braucht. Diese Viertelstunde wirkt bei jeder Änderung, bei jedem Pull Request und bei jedem Versuch, einen Fehler nachzustellen. Über ein Jahr gerechnet ist das keine Kleinigkeit, sondern ein Posten, der sich in Kalenderzeit ausdrücken lässt.

Beide Fehler haben dieselbe Ursache: Die Entscheidung wird ohne die Zahlen getroffen, die sie beantworten würden. Wie lange dauert ein vollständiger Durchlauf heute, wie lange ein Durchlauf nach einer kleinen Änderung, wie oft läuft der Build pro Tag, und wie viel davon ist Arbeit, die schon einmal gemacht wurde? Diese vier Werte stehen in eurem CI-Server, sie müssen nur ausgelesen werden.

KI-generiertDieses Bild wurde mit KI erzeugt · Yves Hoppe / KI / cmt
Der direkte Vergleich

Die Kriterien, an denen sich der Alltag unterscheidet

Verglichen werden hier nicht Sprachmittel und Dateiformate, sondern die sechs Eigenschaften, die im Projektalltag Zeit kosten oder sparen: Lesbarkeit für Neue, Geschwindigkeit bei wiederholten Läufen, Beweglichkeit bei Sonderfällen, Nachvollziehbarkeit der Abhängigkeitsversionen, Aufwand für ein gewöhnliches Java-Projekt und der Stand der Werkzeugkette.

Maven

fester Ablauf aus vorgegebenen Phasen, Beschreibung in XML, aktuell 3.9.16 als allgemein verfügbare Fassung, Maven 4 noch im Release-Kandidaten-Stadium

Gradle

Aufgabengraph statt fester Phasen, Beschreibung in Kotlin oder Groovy, aktuell 9.7.0, mit Zwischenspeichern für Aufgaben und Konfiguration

Wie schnell versteht jemand Neues ein fremdes Projekt?

Maven

Der Ablauf ist überall derselbe, das Verzeichnisschema ebenfalls, und die Datei beschreibt, was gebaut wird, nicht wie. Wer schon einmal ein Maven-Projekt gesehen hat, findet sich in jedem anderen sofort zurecht.

Gradle

Ein schlanker Build mit den Standard-Plugins liest sich gut. Sobald eigene Aufgaben, Konventions-Plugins und Logik dazukommen, ist die Build-Beschreibung Anwendungscode und wird auch so gelesen, also mit dem entsprechenden Zeitaufwand.

Wie schnell läuft ein Durchlauf, wenn sich wenig geändert hat?

Maven

Jede Phase läuft, ob sie muss oder nicht. Parallelität gibt es über Module, und mit dem Maven Daemon lässt sich der Start beschleunigen. Der große Hebel, Arbeit ganz zu überspringen, fehlt aber im Kern.

Gradle

Aufgaben werden übersprungen, wenn sich ihre Eingaben nicht geändert haben. Dazu kommen ein Build-Cache, der auch über Rechner hinweg funktioniert, und ein Configuration Cache, der den Aufbau des Aufgabengraphen einspart.

Wie leicht baust du einen Schritt ein, den kein Standard-Plugin abdeckt?

Maven

Möglich ist es über ein eigenes Plugin oder über die Einbindung eines Skripts. Beides funktioniert, fühlt sich aber wie ein Umweg an, weil der Ablauf aus festen Phasen besteht und jeder Sonderschritt an eine davon gehängt werden muss.

Gradle

Eine Aufgabe mit Eingaben, Ausgaben und Abhängigkeiten zu anderen Aufgaben ist der vorgesehene Fall und keine Ausnahme. Deklarierte Ein- und Ausgaben sind dabei nicht Kür: Erst sie erlauben es dem Werkzeug, die Aufgabe beim nächsten Mal zu überspringen.

Wie leicht siehst du, warum eine bestimmte Bibliotheksversion im Build landet?

Maven

Maven nimmt die nächstliegende Festlegung im Abhängigkeitsbaum, also die Version, deren Pfad zu eurem Projekt am kürzesten ist. Bei gleicher Tiefe gewinnt die zuerst deklarierte. Das ist nachvollziehbar, hängt aber an Reihenfolge und Tiefe und überrascht deshalb regelmäßig.

Gradle

Gradle betrachtet alle angeforderten Versionen im Graphen und wählt standardmäßig die höchste. Das ist unabhängig von Reihenfolge und Tiefe, und mit Constraints lässt sich eine Version verbindlich festlegen, ohne dass jemand eine Abhängigkeit von Hand nach oben ziehen muss.

Wie viel Aufwand kostet ein gewöhnliches Java-Projekt ohne Sonderwünsche?

Maven

Fast keinen. Verzeichnisschema, Phasen und Ausgabeform sind vorgegeben, die Datei enthält im Wesentlichen Abhängigkeiten und ein paar Versionsangaben. Über die Struktur wird nicht diskutiert, weil es nichts zu entscheiden gibt.

Gradle

Mit den Standard-Plugins ist ein Java-Projekt ebenfalls in wenigen Zeilen beschrieben. Der Unterschied liegt darin, dass es Entscheidungen gibt, wo Maven keine anbietet, und dass Teams diese Entscheidungen dann auch treffen, meistens unterschiedlich.

Wie ist der Stand der Werkzeugkette gegenüber neuen Java-Versionen?

Maven

Die 3.9er-Reihe wird gepflegt und läuft mit aktuellen Java-Versionen. Maven 4 bringt mit der Trennung von Build-POM und Consumer-POM eine seit Langem gewünschte Änderung, steht aber mit 4.0.0-rc-6 noch vor der Freigabe. Für Projekte heißt das: warten oder bewusst auf einen Kandidaten setzen.

Gradle

Gradle veröffentlicht regelmäßig und nennt in der Kompatibilitätsmatrix für jede Java-Version die früheste passende Fassung. Die aktuelle 9er-Reihe unterstützt Java bis Version 26. Der Preis dafür sind eigene Versionssprünge, die im Projekt Anpassungen verlangen.

Was passt wann

Wenn es ein gewöhnliches Java-Projekt mit wenigen Modulen ist und der Build heute niemanden stört
bleib bei Maven und steck die Zeit in Tests statt in eine Migration ohne Gegenwert.
Wenn ein Durchlauf nach einer kleinen Änderung mehrere Minuten braucht und viele Module zusammenhängen
rechnet Gradle durch, denn genau dort liegt der Vorteil, und er ist messbar.
Wenn außer Java noch Kotlin, Android oder ein Frontend-Build im Projekt liegen
nimm Gradle, das ist an dieser Stelle keine Abwägung mehr.
Wenn der Wunsch nach dem Wechsel von einer Person kommt und niemand sonst das Werkzeug kann
schulst du erst das Team und entscheidest danach, sonst entsteht ein Build mit genau einem Zuständigen.

Vier Werte, und das Werkzeug steht fest

  1. 01 Wie lange dauert ein Durchlauf nach einer Änderung in einer einzigen Klasse?
  2. 02 Wie viele Module hängen wirklich voneinander ab, und wie tief ist der Graph?
  3. 03 Wie viele Sonderschritte enthält euer Ablauf, die kein Standard-Plugin abdeckt?
  4. 04 Liegt außer Java noch etwas im Projekt, etwa Kotlin, Android oder ein Frontend?
  5. 05 Wer im Team kann den Build nach einem Wechsel noch ändern?
Was du mitnimmst

Woran du die Entscheidung tatsächlich festmachst

Sechs Punkte reichen aus, und für keinen davon braucht ihr eine Testmigration. Vier lest ihr aus dem CI-Server aus, zwei aus dem Projekt selbst.

Die Bauzeit trennen nach vollständig und nach Änderung

Miss zwei Werte getrennt: den vollständigen Durchlauf aus einem leeren Arbeitsverzeichnis und den Durchlauf nach einer Änderung in einer einzigen Klasse. Der erste Wert lässt sich mit keinem Werkzeug wesentlich drücken. Der zweite ist genau der, an dem Gradle seinen Vorteil ausspielt, weil es Aufgaben überspringt, deren Eingaben sich nicht geändert haben.

Zählen, wie viele Module tatsächlich voneinander abhängen

Bei einem Projekt aus zwei oder drei Modulen ist die Frage nach paralleler Ausführung und Zwischenspeicherung wenig wert. Ab etwa einem Dutzend Module mit echten Abhängigkeiten untereinander wird sie zur wichtigsten. Zeichnet den Abhängigkeitsgraphen einmal auf, das ist eine halbe Stunde Arbeit und beantwortet die Frage meistens allein.

Den Sonderbedarf im Ablauf ehrlich aufschreiben

Erzeugter Code, Prüfungen gegen fremde Systeme, mehrere Auslieferungsvarianten aus derselben Quelle, Signaturen, Container-Images: All das lässt sich in Maven über Plugins abbilden, aber es fühlt sich an, als würde man gegen das Werkzeug arbeiten. In Gradle ist es eine Aufgabe mit Eingaben und Ausgaben und damit der vorgesehene Fall. Wenn eure Liste hier lang ist, ist die Frage bereits beantwortet.

Prüfen, ob mehr als Java im Spiel ist

Sobald Kotlin, Android, C++ oder ein Frontend-Build mit im Projekt liegen, ist Gradle die naheliegende Wahl, weil es dafür gepflegte Unterstützung mitbringt. Bei einem reinen Java-Projekt ohne diese Nachbarn fällt dieser Punkt weg und mit ihm ein großer Teil der Begründung für den Wechsel.

Den Stand der Werkzeuge gegen eure Java-Version halten

Gradle veröffentlicht eine Kompatibilitätsmatrix: Java 17 ab Gradle 7.3, Java 21 ab 8.5, Java 25 ab 9.1.0, Java 26 ab 9.4.0. Bei Maven ist 3.9.16 die aktuelle allgemein verfügbare Fassung, Maven 4 steht mit 4.0.0-rc-6 noch vor der Freigabe und verlangt Java 17. Wer eine neue Java-Version einführen will, prüft diese Zeile zuerst.

Die Frage nach der Nachfolge stellen

Wie viele Leute im Haus können den Build heute lesen und ändern, und wie viele könnten es nach einem Wechsel? Ein Maven-Projekt versteht praktisch jede Java-Person nach fünf Minuten. Ein Gradle-Build mit eigenen Aufgaben und Konventions-Plugins ist Anwendungscode und braucht jemanden, der ihn pflegt. Das ist kein Argument dagegen, aber es ist eine Stelle im Fahrplan.

KI-generiertDieses Bild wurde mit KI erzeugt · Yves Hoppe / KI / cmt

Fester Ablauf gegen Aufgabengraph

Maven beschreibt ein Projekt und überlässt den Ablauf dem Werkzeug. Es gibt einen festen Lebenszyklus aus Phasen, und Plugins hängen sich an diese Phasen. Wer mvn package aufruft, weiß, was passiert, ohne die Datei gelesen zu haben: übersetzen, Tests laufen lassen, Artefakt bauen. Diese Vorhersagbarkeit ist der eigentliche Wert. Sie bedeutet auch, dass eine Datei aus einem fremden Projekt keine Überraschungen enthält, weil sie gar keinen Platz dafür hat.

Gradle beschreibt Aufgaben und ihre Abhängigkeiten untereinander. Daraus entsteht ein Graph, den das Werkzeug vor jedem Lauf aufbaut und dann abarbeitet. Der Vorteil liegt darin, dass jede Aufgabe ihre Eingaben und Ausgaben kennt und deshalb übersprungen werden kann, wenn sich nichts geändert hat. Der Nachteil liegt darin, dass die Beschreibung dieses Graphen in einer echten Programmiersprache erfolgt, und alles, was in einer Programmiersprache geschrieben ist, kann kompliziert werden.

Diese beiden Bauweisen erklären die meisten Unterschiede im Alltag. Wenn jemand fragt, warum ein Maven-Build eine Aufgabe wiederholt, die schon erledigt ist, lautet die Antwort: weil die Phase durchläuft. Wenn jemand fragt, warum ein Gradle-Build sich anders verhält als erwartet, lautet die Antwort meistens: weil in der Konfigurationsphase Logik steht, die dort niemand vermutet hätte. Beides sind Eigenschaften und keine Fehler.

Warum Gradle-Builds schneller sind und wann nicht

Der Geschwindigkeitsvorteil hat drei Quellen, und keine davon wirkt beim ersten Lauf. Die erste ist die inkrementelle Ausführung: Eine Aufgabe, deren Eingaben unverändert sind, wird übersprungen. Die zweite ist der Zwischenspeicher für Bauartefakte, der auch Ergebnisse von anderen Rechnern wiederverwenden kann, was im CI-Server und bei mehreren Branches viel bringt. Die dritte ist der Zwischenspeicher für die Konfiguration, der den Aufbau des Aufgabengraphen einspart und damit die Zeit, die vor der eigentlichen Arbeit vergeht.

Alle drei setzen voraus, dass der Build sich an die Regeln hält. Aufgaben müssen ihre Eingaben und Ausgaben vollständig deklarieren, sonst überspringt das Werkzeug etwas, das es nicht hätte überspringen dürfen, oder es baut ständig neu, obwohl nichts passiert ist. Ein Build, der in der Konfigurationsphase Dateien liest, Umgebungsvariablen abfragt oder Zufallswerte erzeugt, kann den Konfigurationsspeicher nicht nutzen. Das ist die häufigste Ursache dafür, dass ein Gradle-Projekt langsam bleibt, obwohl alle Schalter gesetzt sind.

Auf der Maven-Seite gibt es Gegenmittel, sie greifen nur nicht so weit. Module lassen sich parallel bauen, und der Maven Daemon hält einen Prozess am Leben und spart damit die Startzeit der virtuellen Maschine. Was fehlt, ist die Fähigkeit, einzelne Arbeitsschritte anhand ihrer Eingaben zu überspringen. Für ein kleines bis mittleres Projekt fällt das nicht ins Gewicht. Für ein Projekt mit zwanzig Modulen und einem Testbestand, der Minuten braucht, fällt es täglich ins Gewicht.

Abhängigkeitsversionen: nächstliegende Festlegung gegen höchste Version

Hier liegt der Unterschied, der im Alltag die meisten Überraschungen erzeugt, und er ist in beiden Dokumentationen sauber beschrieben. Maven wählt bei mehreren angeforderten Versionen derselben Bibliothek die nächstliegende Festlegung, also die Version, deren Pfad zu eurem Projekt im Abhängigkeitsbaum am kürzesten ist. Liegen zwei Kandidaten gleich tief, gewinnt die zuerst deklarierte. Das lässt sich immer erklären, aber es hängt an der Struktur des Baums und an der Reihenfolge der Einträge, und beides ändert sich, ohne dass jemand es beabsichtigt.

Gradle betrachtet stattdessen alle angeforderten Versionen im Graphen und wählt standardmäßig die höchste davon. Das ist unabhängig von Tiefe und Reihenfolge und deshalb leichter vorherzusagen. Es bringt eine andere Eigenschaft mit: Eine Abhängigkeit weit unten im Baum kann eine Bibliothek nach oben ziehen, ohne dass es in eurer Datei sichtbar wäre. Deshalb gibt es Constraints, mit denen ihr eine Version verbindlich festlegt, egal wer sie anfordert.

Praktisch heißt das für beide Werkzeuge dasselbe: Verlasst euch nicht auf die Vermittlung, sondern legt die Versionen fest, die euch wichtig sind. In Maven ist das der Abschnitt dependencyManagement, oft über ein importiertes BOM. In Gradle sind es Plattformen und Version Catalogs, mit denen sich Versionen an einer Stelle für alle Module bündeln lassen. Beides ist wenig Arbeit und erspart die Diskussion darüber, warum im CI-Server eine andere Version landet als auf dem Rechner nebenan.

Was ein Wechsel kostet und wann er sich rechnet

Ein Wechsel ist kein Übersetzungsvorgang, sondern eine Neuformulierung. Die Abhängigkeiten und die Verzeichnisstruktur lassen sich weitgehend übernehmen, denn Gradle kennt dasselbe Schema. Was neu entsteht, ist alles, was in eurem bisherigen Build über Plugins geregelt war: Erzeugung von Code, Signaturen, Container-Images, Veröffentlichung, Prüfwerkzeuge. Für die verbreiteten Fälle gibt es Entsprechungen, für die eigenen schreibt ihr Aufgaben. Rechne mit einem Vielfachen dessen, was die reine Umstellung der Abhängigkeiten kostet.

Dazu kommt ein Punkt, den Teams regelmäßig unterschätzen: Ein Wechsel ist nur dann fertig, wenn niemand mehr das alte Werkzeug braucht. Ein Zwischenzustand mit zwei parallelen Builds ist der teuerste aller Zustände, weil jede Änderung an zwei Stellen gemacht und an zwei Stellen geprüft werden muss. Wenn ihr wechselt, dann in einem Zug und mit einem Termin, an dem die alten Dateien gelöscht werden.

Rechnen tut sich der Wechsel dort, wo die Zahlen es hergeben: bei vielen Modulen, langen Durchlaufzeiten und einem Ablauf, der über das Übliche hinausgeht. Er rechnet sich nicht, weil ein Werkzeug moderner klingt, weil die Beispiele im Netz häufiger in der einen Sprache geschrieben sind oder weil eine Person es besser kennt. Diese Gründe sind nachvollziehbar, aber sie tragen die Kosten nicht, und sie halten der Frage nicht stand, wer den Build in drei Jahren pflegt.

Dazu passende Kurse

Wenn der Build im Team nur noch von einer Person verstanden wird, sind Java-Seminare rund um Build und Abhängigkeiten der schnellste Weg, diesen Engpass aufzulösen.

Soll der Build am Ende auch ausliefern und nicht nur übersetzen, findest du bei cmt Kurse zum Zusammenspiel von Build und Auslieferung .

Wissen prüfen

Wie sicher bist du beim Thema wirklich?

Lesen fühlt sich schnell nach Können an. Ein kurzer Test zeigt dir, was davon schon sitzt und wo sich ein Kurs lohnt. Kostenlos, ohne Anmeldung, mit einer Erklärung zu jeder Antwort.

Sehr umfangreicher Überblick über Zusammenhänge und Anwendungsbereiche. Sehr zu empfehlen.
Einführung in die Programmierung für Programmiereinsteiger
Effektives, gemeinsames Erarbeiten der Aufgaben mit vielen praktischen Tipps.
Groovy Grundkurs
Sehr guter Trainer und gute Aufbereitung der Inhalte und auch eingehen auf Fragen der Beteiligten.
Unreal Engine Kurs für Einsteiger

Häufige Fragen

Ist Gradle grundsätzlich schneller als Maven?
Beim vollständigen Durchlauf aus einem leeren Arbeitsverzeichnis kaum. Der Unterschied entsteht bei wiederholten Läufen, weil Gradle Aufgaben überspringt, deren Eingaben unverändert sind, und weil es Ergebnisse zwischenspeichert. Wenn euer Build ohnehin selten läuft oder klein ist, kommt dieser Vorteil bei euch nicht an.
Sollen wir auf Maven 4 warten?
Maven 4 ist noch nicht allgemein verfügbar, aktuell steht 4.0.0-rc-6 als Release-Kandidat bereit und verlangt Java 17. Die wichtigste Änderung ist die Trennung von Build-POM und Consumer-POM, die einige lang gewünschte Verbesserungen möglich macht, ohne das Ökosystem zu brechen. Für ein laufendes Projekt gibt es keinen Grund zur Eile: Die 3.9er-Reihe wird gepflegt und läuft mit aktuellen Java-Versionen.
Können wir Maven und Gradle im selben Haus nebeneinander betreiben?
Ja, und in größeren Häusern ist das der Normalfall. Innerhalb eines Projekts sollte es aber nur eines geben, weil sonst jede Änderung doppelt gemacht wird. Was ihr über Projektgrenzen hinweg vereinheitlichen solltet, sind die Dinge davor und danach: die Versionsangaben eurer internen Bibliotheken, die Registry, in der die Artefakte liegen, und die Prüfschritte im CI-Server.
Warum landet auf dem CI-Server eine andere Bibliotheksversion als bei uns lokal?
Fast immer, weil die Version nicht festgelegt ist und die Vermittlung im Abhängigkeitsbaum sie bestimmt. In Maven entscheidet die nächstliegende Festlegung und bei gleicher Tiefe die Reihenfolge, in Gradle die höchste angeforderte Version. Der Ausweg ist derselbe: die relevanten Versionen zentral festlegen, in Maven über die Verwaltung von Abhängigkeiten, in Gradle über Constraints und Version Catalogs.
Brauchen wir für Gradle unbedingt Kotlin?
Nein, Groovy funktioniert weiter. Die Kotlin-Variante hat den Vorteil, dass die Entwicklungsumgebung Fehler beim Schreiben anzeigt und Vervollständigung anbietet, was bei einem Build, der wächst, viel wert ist. Wenn euer Build klein bleibt, ist die Sprachwahl die geringste eurer Entscheidungen.
Persönlich für dich da

Deine Ansprechpartner

Du bist dir nicht sicher, welcher Kurs oder welches Level zu dir passt? Wir beraten dich persönlich und kostenlos.

Yves Hoppe

Yves Hoppe

Weiterbildung & Beratung

Hilft dir, aus dem Programmierung-Programm den passenden Kurs für deinen Stand zu finden.

Norbert Jansen

Norbert Jansen

Beratung & Inhouse

Plant mit dir Inhouse-Trainings, die auf eure Abläufe und euren Datenbestand zugeschnitten sind.

Den eigenen Build einmal von innen ansehen

Bei cmt gehst du im Kurs zum jeweiligen Build-Werkzeug den kompletten Ablauf durch, von den Abhängigkeiten bis zur Auslieferung, und siehst dabei, welche Zeit im Durchlauf wo verloren geht.