I tweet technical content that I consider interesting, but the funny tweets are the ones that get the most engagement. I attended the JavaLand conference in March, stumbled upon the Gradle booth, and found this gem:
Of course, at some point, a fanboy hijacked the thread and claimed the so-called superiority of Gradle. In this post, I'd like to shed some light on my stance, so I can direct people to it instead of debunking the same "reasoning" repeatedly.
To manage this, I need to get back in time. Software development is a fast-changing field, and much of our understanding is based on personal experience. So here's mine.
My first build tool: Ant
I started developing in Java in 2002. At the time, there were no build tools: we compiled and built through the IDE. For the record, I first used Visual Age for Java; then, I moved to Borland JBuilder.
Building with an IDE has a huge issue: each developer has dedicated settings, so artifact generation depends on the developer-machine combination.
Non-repeatable builds are an age-old problem. My first experience with repeatable builds is Apache Ant:
Apache Ant is a Java library and command-line tool whose mission is to drive processes described in build files as targets and extension points dependent upon each other. The main known usage of Ant is the build of Java applications. Ant supplies a number of built-in tasks allowing to compile, assemble, test and run Java applications. Ant can also be used effectively to build non Java applications, for instance C or C++ applications. More generally, Ant can be used to pilot any type of process which can be described in terms of targets and tasks.
Ant is based on three main abstractions:
- A task is an atomic unit of work, e.g.,
javac
to compile Java files,war
to assemble a Web Archive, etc. Ant provides lots of tasks out-of-the-box but allows adding custom ones. - A target is a list of tasks
- You can define dependencies between tasks, such as package depending on compile. In this regard, you can see Ant as a workflow execution engine.
I soon became "fluent" in Ant. As a consultant, I went from company to company, project to project. Initially, I mostly set up Ant, but Ant became more widespread as time passed, and I encountered existing Ant setups. I was consistent in my projects, but other projects were very different from each other.
Every time, when arriving at a new project, you had to carefully read the Ant setup to understand the custom build. Moreover, each project's structure was different. Some put their sources in src
, some in sources
, some in a nested structure, etc.
I remember once a generic build file that tried accommodating the whole of an organization's project needs. It defined over 80 targets in over 2,000 lines of XML. It took me a non-trivial amount of time to understand how to use it with help and even more time to be able to tweak it without breaking projects.
My second build tool: Maven
The above project got me thinking a lot. I wanted to improve the situation as the maintainers had already pushed Ant's limits. At the time, I was working with my friend Freddy Mallet (of Sonar fame). We talked, and he pointed me to Maven. I had once built a project with Maven but had no other prior experience. I studied the documentation for hours, and through trial-and-error attempts, under the tutelage of Freddy, migrated the whole Ant build file to a simple parent POM.
In Ant, you'd need to define everything in each project. For example, Ant requires configuring the Java files location for compilation; Maven assumes they are under src/main/java
, though it's possible to override it. Maven did revolutionize the Java build field with its Convention over Configuration approach. Nowadays, lots of software offer sensible configuration by default.
For developers who go from project to project, as I did, it means there's much less cognitive load when joining a new project. I expect Java sources to be located under src/main/java
. Maven conventions continue beyond the project's structure. They also define the project's lifecycle, from compilation to uploading the artifact in a remote registry, via unit and integration testing.
Finally, junior developers tend to be oblivious about it, but Maven defined the term dependency management. It introduced the idea of artifact registries, where one can download immutable dependencies from and push artifacts to. Before that time, each project had to store dependencies in its dedicated repository.
For the record, there were a couple of stored dependencies on the abovementioned project. When I migrated from Ant to Maven, I had to find the exact dependency version. For most, it was straightforward, as it was in the filename or the JAR's manifest. One, however, had been updated with additional classes. So much for immutability.
Maven had a profound influence on all later build tools: they defined themselves in reference to Maven.
No build tool of mine: Gradle
Gradle's primary claim was to fix Maven's shortcomings, or at least what it perceived as such. While Maven is not exempt from reproach, Gradle assumed the most significant issue was its lack of flexibility. It's a surprising assumption because that was precisely what Maven improved over Ant. Maven projects have similar structures and use the same lifecycle: the principle of least surprise in effect. Conversely, Gradle allows customizing nearly every build aspect, including the lifecycle.
Before going to confront the flexibility argument, let me acknowledge two great original Gradle features that Maven implemented afterward: the Gradle daemon and the Gradle wrapper.
Maven and Gradle are both Java applications that run on the JVM. Starting a JVM is expensive in terms of time and resources. The benefit is that long-running JVM will optimize the JIT-ed code over time. For short-term tasks, the benefit is zero and even harmful if you take the JVM startup time into account. Gradle came up with the Gradle daemon. When you run Gradle, it will look for a running daemon. If not, it will start a new one. The command-line app will delegate everything to the daemon. As its name implies, the daemon doesn't stop when the command line has finished. The daemon leverages the benefits of the JVM.
Chances are that your application will outlive your current build tools. What happens when you need to fix a bug five years from now, only to notice that the project's build tool isn't available online? The idea behind Gradle's wrapper is to keep the exact Gradle version along with the project and just enough code to download the full version over the Internet. As a side-effect, developers don't need to install Gradle locally; all use the same version, avoiding any discrepancy.
Debunking Gradle's flexibility
Gradle brought the two above great features that Maven integrated, proving that competition is good. Despite this, I still find no benefit of Gradle.
I'll try to push the emotional side away. At its beginning, Gradle marketing tried to put down Maven on every possible occasion, published crazy comparison charts, and generally was very aggressive in its communication. Let's say this phase lasted far more than would be acceptable for a young company trying to find its place in the market. You could say that Gradle was very Oedipian in its approach: trying to kill its Maven "father". Finally, after all those years, it seems it has wised up and now "loves Maven".
Remember that before Maven took over, every Ant project was ad hoc. Maven did put an end to that. It brought law to the World Wild West of custom projects. You can disagree with the law, but it's the law anyway, and everybody needs to stand by it. Maven standards are so entrenched that even though it's possible to override some parameters, e.g., source location, nobody ever does it.
I did experience two symptoms of Gradle's flexibility. I suspect far more exist.
Custom lifecycle phases
Maven manages integration testing in four phases, run in order:
-
pre-integration-test
: set up anything the tests need -
integration-test
: execute the tests -
post-integration-test
: clean up the resources, if any -
verify
: act upon the results of the tests
I never used the pre- and post-phases, as each test had a dedicated setup and teardown logic.
On the other side, Gradle has no notion of integration tests whatsoever. Yet, Gradle fanboys will happily explain that you can add the phases you want. Indeed, Gradle allows lifecycle "customization": you can add as many extra phases into the regular lifecycle as you want.
It's a mess, for each project will need to come up with both the number of phases required and their name: integration-test
, integration-tests
, integration-testing
, it
(for the lazy), etc. The options are endless.
The snowflake syndrome
Maven treat every project as a regular standard project. And if you have specific needs, it's possible to write a plugin for that. Writing a Maven plugin is definitely not fun; hence, you only write one when it's necessary, not just because you have decided that the law doesn't apply to you.
Gradle claims that lack of flexibility is an issue; hence, it wants to fix it. I stand by the opposite: lack of flexibility for my build tool is a feature, not a bug. Gradle makes it easy to hack the build. Hence, anybody who thinks their project is a special snowflake and deserves customization will happily do so. Reality check: it's rarely the case; when it is, it's for frameworks, not regular projects. Gradle proponents say that it still offers standards while allowing easy configuration. The heart of the matter is that it's not a standard if it can be changed at anybody's whim.
Gradle is the de facto build tool for Android projects. In one of the companies I worked for, somebody wrote custom Groovy code in the Gradle build to run Sonar and send the metrics to the internal Sonar instance. There was no out-of-the-box Sonar plugin at the time, or I assume it didn't cut it. So far, so good.
When another team created the company's second Android project, they copy-pasted the first project's structure and the build file. The intelligent thing to do would have been, at this time to make an internal Gradle plugin out of the Sonar-specific code. But they didn't do it because Gradle made it so easy to hack the build. And I, the Gradle-hater, took it upon myself to create the plugin. It could have been a better developer experience, to say the least. Lacking quality documentation and using an untyped language (Groovy), I used the console to print out the objects' structure to progress.
Conclusion
Competition is good, and Gradle has brought new ideas that Maven integrated, the wrapper and the daemon. However, Gradle is built on the premise that flexibility is good, while my experience has shown me the opposite. Ant was very flexible, and the cognitive load to go from one project to the next was high.
We, developers, are human beings: we like to think our projects are different from others. Most of the time, they are not. Customization is only a way to satisfy our ego. Flexible build tools allow us to implement such customization, whether warranted or not.
Irrelevant customizations bring no benefit and are easy to develop but expensive to maintain. If managing software assets is part of my responsibilities, I'll always choose stability over flexibility for my build tool.
Originally published at A Java Geek on August 6th, 2023
Top comments (16)
I took a similar road. First Ant, then Ivy and then Maven.
At some point, I was fed up with XML not really understanding the benefits so I started looking into Gradle, being envious not being able to use it.
Then, we needed an Android App so I was happy to start with the (mandatory) Gradle build.
First, there were all kinds of plugins missing we needed. So I hacked them into the build script. I ended up, using printf debugging to find bugs in my build script. Something that would never be required or even possible with Maven.
One thing I found particular weird was the mix-up of declarative and imperative code that got me confused more than once.
I also thought that this flexibility is great but it isn’t. You have enough work to fix your code (and potentially pipeline code) that you surely don’t wan to fix build script bugs…
Exactly my thoughts. Thanks, I will also point to your article from now on when needed 🥰
I'm very happy that I'm not alone in my thinking's.
Standardization is key and I think it's main key for open-source, where you can "feel at home" as much as possible on every project that uses Maven. This helps reduce time to contribute, witch I think is the main metric for open source community.
Also, if some day Apache move main maven repo to modern host e.g. GitHub it will increase the love. NOT MIRROR
What do you mean here? The Maven repos for Apache Maven itself and for all plugins and components are available on Github ....they accept PR etc.
for example:
github.com/apache/maven
github.com/apache/maven-install-pl...
etc.
GitHub is a pretty crappy UX. Partly the fault of Git being crappy UX, but GiHub could be so much better.
Use of Maven certainly doesn't make me "feel at home", the first thing I do when encountering a Maven project is convert it to Gradle so I can breath a sigh of relief.
About GitHub:
My point was that GitHub is better comparing to what they use now link 1, link 2. This is very old approach. I believe they also use mailing lists for discussion, this is very old style and it will not get new contributors.
Even Gnome right now switched to GitLab and feels good there:
About "feel at home":
With Maven you will have more probability that next project will have same structure and flow and not fall in "The snowflake syndrome" like described in article.
What do you mean? We, the Apache Maven team (including myself) is using GitHub for a long time ...
one exmaple: github.com/apache/maven
maven.apache.org/plugins/
maven.apache.org/shared/
maven.apache.org/pom/index.html
maven.apache.org/extensions/index....
This list all plugins etc. incl. links to GitHub etc. incl. link to JIRA ticket system.. you can offer pull requests via Github... for a long time.
My path to Maven was similar, but started a bit earlier. I went from: make (in the '90s) to Ant to Maven. There wasn't really anything good pre-Ant. Make isn't really suitable for Java but nothing particularly better at the time, and you can make make work if you need to. I stuck with Ant probably longer than I should have before going to Maven. I haven't used Gradle at all.
Indeed! That is an exciting conclusion.
My final thought why I like Maven it's just because I am old school guy, but now, I have a better way to justify it.
I am in the opposite camp. While I do recognize Maven's legacy and all the good that it brought, I would choose Gradle any day. This probably primarily comes from my internal burning hate for everything XML, but I also find that Gradle runs a lot faster. I don't have any other objective arguments, and I haven't written plugins for either tool, but I gotta say, I have a lot more will to live after migrating my projects to Gradle 4 years ago.
I had a similar journey. First Ant, then Maven, then Gradle. Gradle is by far better for how I work. Maven offered roadblocks all the time. Maven POM files are horrendously verbose and awkward to deal with.
Oh? Which of the "standard" projects do you mean?
mvn archetype:generate
lists over 3000 project types. Good luck.Gradle is built on the premise that flexibility is required, as demonstrated by the over 3000 Maven archetypes and pretty much the experience of every developer that has done something more than "Hello World". I don't consider Gradle the last word in build tools, it certainly has it's rough spots, but it works for me. I'll always choose flexibility over Maven's handcuffs. I'm not going back to that hell.
I think point in article and I'm agree with it, that flexibility creates another hell.
So you on the road to the hell too with your flexibility.
More on that, by being loud - you are pushing young people to that hell too.
Try to think broadly and analyse historical consequences of some directions and to what they lead in long term run.
Author where did great job neutrally describing things.
Gradle brings nice features into Maven and this is good in scope of progress, so Gradle did good job here, but what is question mark here, after so many years is this tool that we need to follow?
I will be as loud as possible to save people from Maven hell. They can thank me later. There is an article out there on the internet "Why everyone eventually hates Maven" - it's still relevant.
I don't disagree that the flexibility can create another hell. I just don't see it happening very often in practice. With Maven the hell is enforced. With Gradle you have enough flexibility to cause problems if you aren't careful, but in most cases you simply have the tools you need to get things done.
If you are a beginner, follow the conventions strictly. Don't roll your own solutions until you have a thorough understanding. Both Maven and Gradle prefer convention over configuration. Gradle just does it in a way that is easier to read, far less to write, and leaves you an escape hatch if you find that you need it in the future.
The build output from Maven is overly verbose too the point that it makes it difficult to find crucial information when things go wrong. Gradle does a better job.
The author doesn't appear neutral to me, there is an anti-Gradle bias in the article that IMO is not justified. Maven works in simple cases and when you realize that you need something more, trying to accomplish it with Maven is often painful. I was there. I tried. I evolved the same project through Ant, Maven, and Gradle. With Gradle it was simply far less painful. Of those three options I consider only Gradle when starting a project. I know the other two will let me down. Not that Gradle hasn't let me down at times as well. I admit it can be complex, but it isn't always complex. Gradle projects can be simple and straightforward, and usually they are.
Over 3000 archetypes for Maven projects... that's basically demonstrating the flaw in that approach.
First I'm completely biased for Maven...
That is a contradiction in it self, because not everyone hates it..
The flexibility is a thing I'm always reading.. but do you really need that (apart from that you can write plugins etc.)..
The output of Maven can be reduced if you like?
And Maven works for a lot of cases .. not only simple case I'm doing builds for companies with builds with over 3000+ modules etc. (Is that simple?).. complex setups etc. and yes written a number of plugins for such things which integrates very well in the overall build process. This integration makes it easy for other devs to understand the whole build process... In general the default plugins should do the job ... my experience if you need to outside most of the time you should reconsider that...
I don't say Maven is perfect... it can be improved in many ways ...
3000 archetypes is not related to Maven itself. Many people/orgs offer archteypes...
thanks for the article !, I am currently considering also sbt
Don't forget archetypes