Ich twittere technische Inhalte, die ich für interessant halte, aber die lustigen Tweets sind diejenigen, die das meiste Interesse wecken. Ich habe im März an der JavaLand-Konferenz teilgenommen, bin zufällig auf den Gradle-Stand gestoßen und habe dieses Juwel gefunden:
Natürlich hat irgendwann ein Fanboy den Thread gekapert und die sogenannte Überlegenheit von Gradle behauptet. In diesem Beitrag möchte ich etwas Licht auf meine Haltung werfen, damit ich die Leute darauf aufmerksam machen kann, anstatt immer wieder dieselben „Argumentation“ zu entlarven.
Um das zu bewältigen, muss ich rechtzeitig zurückkommen. Softwareentwicklung ist ein sich schnell veränderndes Feld und ein Großteil unseres Verständnisses basiert auf persönlichen Erfahrungen. Also hier ist meiner.
Ich habe 2002 mit der Entwicklung in Java begonnen. Damals gab es keine Build-Tools: Wir haben über die IDE kompiliert und erstellt. Zur Erinnerung: Ich habe zuerst Visual Age für Java verwendet; Dann wechselte ich zu Borland JBuilder.
Das Erstellen mit einer IDE stellt ein großes Problem dar: Jeder Entwickler verfügt über eigene Einstellungen, daher hängt die Artefaktgenerierung von der Entwickler-Maschinen-Kombination ab.
Nicht wiederholbare Builds sind ein uraltes Problem. Meine erste Erfahrung mit wiederholbaren Builds ist Apache Ant:
Apache Ant ist eine Java-Bibliothek und ein Befehlszeilentool, dessen Aufgabe es ist, in Build-Dateien beschriebene Prozesse als voneinander abhängige Ziele und Erweiterungspunkte zu steuern. Die wichtigste bekannte Verwendung von Ant ist die Erstellung von Java-Anwendungen. Ant bietet eine Reihe integrierter Aufgaben, mit denen Java-Anwendungen kompiliert, assembliert, getestet und ausgeführt werden können. Ant kann auch effektiv zum Erstellen von Nicht-Java-Anwendungen verwendet werden, beispielsweise C- oder C++-Anwendungen. Allgemeiner gesagt kann Ant zur Steuerung jeder Art von Prozess verwendet werden, der in Form von Zielen und Aufgaben beschrieben werden kann.
Ant basiert auf drei Hauptabstraktionen:
javac
zum Kompilieren von Java-Dateien, war
zum Zusammenstellen eines Webarchivs usw. Ant stellt viele Aufgaben sofort bereit, ermöglicht jedoch das Hinzufügen benutzerdefinierter Aufgaben.
Ich konnte Ant bald „fließend“ sprechen. Als Berater bin ich von Unternehmen zu Unternehmen, von Projekt zu Projekt gegangen. Anfangs habe ich hauptsächlich Ant eingerichtet, aber mit der Zeit verbreitete sich Ant immer weiter und ich stieß auf bestehende Ant-Setups. Ich war in meinen Projekten konsequent, aber andere Projekte unterschieden sich stark voneinander.
Jedes Mal, wenn man zu einem neuen Projekt kam, musste man die Ant-Einrichtung sorgfältig lesen, um den benutzerdefinierten Build zu verstehen. Darüber hinaus war die Struktur jedes Projekts unterschiedlich. Einige legen ihre Quellen in src
ab, einige in sources
, einige in einer verschachtelten Struktur usw.
Ich erinnere mich an eine generische Build-Datei, die versuchte, alle Projektanforderungen einer Organisation zu erfüllen. Es definierte über 80 Ziele in über 2.000 XML-Zeilen. Ich brauchte nicht unerheblich viel Zeit, um zu verstehen, wie man es mit Hilfe nutzt, und noch mehr Zeit, um es zu optimieren, ohne Projekte zu zerstören.
Das obige Projekt hat mich sehr zum Nachdenken gebracht. Ich wollte die Situation verbessern, da die Betreuer die Grenzen von Ant bereits überschritten hatten. Zu dieser Zeit arbeitete ich mit meinem Freund Freddy Mallet (von Sonar) zusammen. Wir unterhielten uns und er verwies mich auf Maven. Ich hatte einmal ein Projekt mit Maven erstellt, hatte aber keine anderen Vorkenntnisse. Ich habe die Dokumentation stundenlang studiert und durch Versuch und Irrtum unter der Anleitung von Freddy die gesamte Ant-Build-Datei auf ein einfaches übergeordnetes POM migriert.
In Ant müssten Sie alles in jedem Projekt definieren. Ant erfordert beispielsweise die Konfiguration des Speicherorts der Java-Dateien für die Kompilierung; Maven geht davon aus, dass sie sich unter src/main/java
befinden, obwohl es möglich ist, dies zu überschreiben. Maven hat mit seinem „Convention over Configuration“ -Ansatz den Java-Build-Bereich revolutioniert. Heutzutage bieten viele Softwareprogramme standardmäßig eine sinnvolle Konfiguration an.
Für Entwickler, die wie ich von Projekt zu Projekt wechseln, bedeutet dies, dass die kognitive Belastung viel geringer ist, wenn sie einem neuen Projekt beitreten. Ich erwarte, dass sich Java-Quellen unter src/main/java
befinden. Maven-Konventionen bleiben über die Projektstruktur hinaus bestehen. Sie definieren auch den Lebenszyklus des Projekts, von der Kompilierung bis zum Hochladen des Artefakts in eine Remote-Registrierung, über Unit- und Integrationstests.
Schließlich neigen junge Entwickler dazu, sich dessen nicht bewusst zu sein, aber Maven hat den Begriff Abhängigkeitsmanagement definiert. Es führte die Idee von Artefaktregistern ein, von denen man unveränderliche Abhängigkeiten herunterladen und Artefakte dorthin verschieben kann. Zuvor musste jedes Projekt Abhängigkeiten in seinem eigenen Repository speichern.
Zur Erinnerung: Es gab ein paar gespeicherte Abhängigkeiten zu dem oben genannten Projekt. Als ich von Ant zu Maven migrierte, musste ich die genaue Abhängigkeitsversion finden. Für die meisten war es einfach, da es im Dateinamen oder im JAR-Manifest stand. Eine davon wurde jedoch mit zusätzlichen Klassen aktualisiert.
So viel zur Unveränderlichkeit.
Maven hatte einen tiefgreifenden Einfluss auf alle späteren Build-Tools: Sie definierten sich in Bezug auf Maven.
Gradles Hauptanspruch bestand darin, Mavens Mängel zu beheben, oder zumindest das, was es als solche ansah. Während Maven nicht von Vorwürfen befreit ist, ging Gradle davon aus, dass das größte Problem die mangelnde Flexibilität sei. Das ist eine überraschende Annahme, denn genau das hat Maven gegenüber Ant verbessert. Maven-Projekte haben ähnliche Strukturen und verwenden denselben Lebenszyklus: Es gilt das Prinzip der geringsten Überraschung . Umgekehrt ermöglicht Gradle die Anpassung nahezu aller Build-Aspekte, einschließlich des Lebenszyklus.
Bevor ich mich mit dem Flexibilitätsargument befasse, möchte ich zwei großartige ursprüngliche Gradle-Funktionen anerkennen, die Maven später implementiert hat: den Gradle-Daemon und den Gradle-Wrapper.
Maven und Gradle sind beide Java-Anwendungen, die auf der JVM ausgeführt werden. Das Starten einer JVM ist zeit- und ressourcenintensiv. Der Vorteil besteht darin, dass eine JVM mit langer Laufzeit den JIT-Code im Laufe der Zeit optimiert. Bei kurzfristigen Aufgaben ist der Nutzen gleich Null und sogar schädlich, wenn man die JVM-Startzeit berücksichtigt. Gradle hat den Gradle-Daemon entwickelt. Wenn Sie Gradle ausführen, sucht es nach einem laufenden Daemon. Wenn nicht, wird ein neues gestartet. Die Befehlszeilen-App delegiert alles an den Daemon. Wie der Name schon sagt, stoppt der Daemon nicht, wenn die Befehlszeile beendet ist. Der Daemon nutzt die Vorteile der JVM.
Die Chancen stehen gut, dass Ihre Anwendung Ihre aktuellen Build-Tools überdauert. Was passiert, wenn Sie in fünf Jahren einen Fehler beheben müssen und dann feststellen, dass das Build-Tool des Projekts nicht online verfügbar ist? Die Idee hinter dem Gradle-Wrapper besteht darin, die genaue Gradle-Version zusammen mit dem Projekt und gerade genug Code aufzubewahren, um die Vollversion über das Internet herunterzuladen. Als Nebeneffekt müssen Entwickler Gradle nicht lokal installieren; Alle verwenden die gleiche Version, sodass Abweichungen vermieden werden.
Gradle brachte die beiden oben genannten großartigen Funktionen mit, die Maven integriert hat, und beweist damit, dass der Wettbewerb gut ist. Trotzdem finde ich immer noch keinen Nutzen von Gradle.
Ich werde versuchen, die emotionale Seite zu verdrängen. Zu Beginn versuchte das Gradle-Marketing bei jeder Gelegenheit, Maven niederzuschlagen, veröffentlichte verrückte Vergleichstabellen und war im Allgemeinen sehr aggressiv in der Kommunikation. Nehmen wir an, diese Phase hat viel länger gedauert, als es für ein junges Unternehmen, das sich auf dem Markt behaupten will, akzeptabel wäre. Man könnte sagen, dass Gradle in seiner Herangehensweise sehr ödipianisch war: Er versuchte, seinen Maven-„Vater“ zu töten. Endlich, nach all den Jahren, scheint es, als wäre es schlauer geworden und „liebt Maven“.
Denken Sie daran, dass vor der Übernahme durch Maven jedes Ant-Projekt ein Ad-hoc-Projekt war. Maven hat dem ein Ende gesetzt. Es brachte Gesetz in den Wilden Westen der kundenspezifischen Projekte. Man kann mit dem Gesetz nicht einverstanden sein, aber es ist trotzdem das Gesetz, und jeder muss dazu stehen. Maven-Standards sind so tief verwurzelt, dass es zwar möglich ist, einige Parameter, z . B. den Quellort, zu überschreiben, dies jedoch niemand tut.
Ich habe zwei Symptome von Gradles Flexibilität festgestellt. Ich vermute, dass es weitaus mehr davon gibt.
Maven verwaltet Integrationstests in vier Phasen, die in der folgenden Reihenfolge ausgeführt werden:
pre-integration-test
: Richten Sie alles ein, was die Tests benötigenintegration-test
: Führen Sie die Tests auspost-integration-test
: Bereinigen Sie die Ressourcen, falls vorhandenverify
: auf die Ergebnisse der Tests reagieren
Ich habe die Vor- und Nachphasen nie verwendet, da jeder Test eine eigene Auf- und Abbaulogik hatte.
Andererseits hat Gradle überhaupt keine Ahnung von Integrationstests . Gradle-Fans erklären Ihnen jedoch gerne, dass Sie die gewünschten Phasen hinzufügen können. Tatsächlich ermöglicht Gradle die „Anpassung“ des Lebenszyklus: Sie können so viele zusätzliche Phasen zum regulären Lebenszyklus hinzufügen, wie Sie möchten.
Es ist ein Chaos, denn jedes Projekt muss sich sowohl die Anzahl der erforderlichen Phasen als auch deren Namen ausdenken: integration-test
, integration-tests
, integration-testing
, it
(für die Faulen) usw. Die Möglichkeiten sind endlos.
Maven behandelt jedes Projekt als reguläres Standardprojekt. Und wenn Sie spezielle Anforderungen haben, ist es möglich, ein Plugin dafür zu schreiben. Ein Maven-Plugin zu schreiben macht definitiv keinen Spaß; Daher schreiben Sie nur dann eine, wenn es nötig ist, und nicht nur, weil Sie entschieden haben, dass das Gesetz für Sie nicht gilt.
Gradle behauptet, dass mangelnde Flexibilität ein Problem sei; Daher möchte es das Problem beheben. Ich stehe zum Gegenteil: Mangelnde Flexibilität meines Build-Tools ist ein Feature und kein Fehler. Gradle macht es einfach, den Build zu hacken. Daher wird jeder, der der Meinung ist, dass sein Projekt eine besondere Schneeflocke ist und eine individuelle Anpassung verdient, dies gerne tun. Realitätscheck: Das ist selten der Fall; Wenn dies der Fall ist, handelt es sich um Frameworks, nicht um reguläre Projekte. Gradle-Befürworter sagen, dass es immer noch Standards bietet und gleichzeitig eine einfache Konfiguration ermöglicht. Der Kern der Sache ist, dass es kein Standard ist, wenn er nach Lust und Laune von irgendjemandem geändert werden kann.
Gradle ist das De-facto -Build-Tool für Android-Projekte . In einem der Unternehmen, für die ich gearbeitet habe, hat jemand benutzerdefinierten Groovy-Code im Gradle-Build geschrieben, um Sonar auszuführen und die Metriken an die interne Sonar-Instanz zu senden. Damals gab es kein sofort einsatzbereites Sonar-Plugin, oder ich gehe davon aus, dass es nicht funktioniert hat. So weit, ist es gut.
Als ein anderes Team das zweite Android-Projekt des Unternehmens erstellte, kopierten sie die Struktur des ersten Projekts und die Build-Datei. Das Intelligenteste wäre zu diesem Zeitpunkt gewesen, aus dem Sonar-spezifischen Code ein internes Gradle-Plugin zu erstellen. Aber sie haben es nicht getan, weil Gradle es so einfach gemacht hat, den Build zu hacken. Und ich, der Gradle-Hasser, habe es mir zur Aufgabe gemacht, das Plugin zu erstellen. Es hätte gelinde gesagt eine bessere Entwicklererfahrung sein können. Mangels hochwertiger Dokumentation und der Verwendung einer untypisierten Sprache (Groovy) habe ich die Struktur der Objekte über die Konsole ausgedruckt, um fortzufahren.
Der Wettbewerb ist gut und Gradle hat neue Ideen mitgebracht, die Maven integriert hat, den Wrapper und den Daemon. Allerdings basiert Gradle auf der Prämisse, dass Flexibilität gut ist, während meine Erfahrung mir das Gegenteil gezeigt hat. Ant war sehr flexibel und die kognitive Belastung, von einem Projekt zum nächsten zu wechseln, war hoch.
Wir Entwickler sind Menschen: Wir glauben gerne, dass sich unsere Projekte von anderen unterscheiden. Meistens sind sie es nicht. Individualisierung ist nur eine Möglichkeit, unser Ego zu befriedigen. Mithilfe flexibler Build-Tools können wir solche Anpassungen implementieren, unabhängig davon, ob dies gerechtfertigt ist oder nicht.
Irrelevante Anpassungen bringen keinen Nutzen und sind einfach zu entwickeln, aber teuer in der Wartung. Wenn die Verwaltung von Software-Assets zu meinen Aufgaben gehört, ziehe ich bei meinem Build-Tool immer die Stabilität der Flexibilität vor.
Ursprünglich veröffentlicht bei A Java Geek am 6. August 2023