DEV Community

Cover image for Why I prefer Maven over Gradle
Arnaud Dagnelies
Arnaud Dagnelies

Posted on • Originally published at blog.angelside.net

Why I prefer Maven over Gradle

In the Java world, one of the first question developers encounter is "should I use Grade or Maven as build tool?". It's a fundamental decision which will stick to you with time. And when googling it, Gradle's biased comparison even pops up as the top search result (at least for me).

At first sight, Gradle looks cool:

  • Their website looks way nicer and polished than Maven's one

  • The syntax is much more compact than Maven's verbose XML

  • Gradle is "newer" while Maven is "older"

  • Gradle is much faster (according to them)

No wonder people pick it up when faced with uncertainty and just wanna get started. So now, let me tell what's wrong with Gradle IMHO and why Maven is still the better option, even so many years later.

Configuration vs Scripting

Basically, the pom.xml that you define in Maven is a "configuration". You define the name, the version, the list of dependencies, etc. Since it follows a specific schema, with a set of properties to define, you can also look at it visually through a UI for example. It's a declarative definition of your library/app.

On the opposite, a Gradle build script is exactly that: a script. It's using the Groovy language, or recently also Kotlin, to let you write anything you want. Let it sink in, you use a programming language to define what the build should do. You can import other scripts, send a HTTP request to check the current weather and insert a funny UI generated picture in your build artifact.

While they have many aspects in common, they nature ("configuration" vs "script") is what differentiates them fundamentally.

You may think: "Isn't it great if I can do anything with that build script?! It's ultimate freedom!". That is right, but this boon is also a curse.

When I see a Maven project, with a pom.xml, I know what it does and where to find what. It's always the same. Directories, commands to run, changing the version, whatever, it's the same for all maven projects.

When I see a Gradle project, I have no idea what the build script does. If you don't have a clear documentation ready, you'll have to dive into the build script to actually discover and try to understand what it exactly does.

The price of freedom

It's not rare that you need something specific in your build. In both Maven and Gradle, it's possible to do so, but here also their approach is opposite.

In Gradle, it's straightforward. Since it's scripting, just write whatever you want, you can do anything very easily. Your own build stages, calling functions, using variables, importing some other scripts, whatever. It's easy.

In Maven, it's the opposite. Adding something custom is more difficult. You will have to use a plugin to enable the specific functionality, or even write a plugin yourself if really necessary. While writing a plugin is definitely more work, this kind of also enforces reusability though.

The takeaway here is the same as before. While Maven builds tend to always follow the same build stages and conventions, Gradle builds tend to become more and more complex and customized over time, because it's so easy to "just add a few lines" to the build script. Look at it after a few years and the Maven pom.xml is likely almost as readable as the first days while the Gradle build.gradle script became rocket science.

As an exercise, I picked a random gradle.build file from another team at work to look at it. It had over thousand lines and the few dependencies it had were externalized in another file and combined in a fancy way.

On the opposite, pick any Maven project, and the list of dependencies will always be in the same place, in the <dependencies>...</dependencies> tag.

History repeating itself. As a side note, it's interesting to see that in the very early days of Java, before maven was born, build scripts were the norm. In the beginning they were plain shell scripts invoking javac ... to compile the source code, packaging it, etc. Then came "ant" to do the same, in a bit more structured way but still tended to become customized and complex over time. Then one day came the idea to use a more declarative approach, by defining a project and its dependencies while letting the tool take care of how it is build. Maven was born. Then, some day, Gradle was born, because "I want to customize stuff".

Gradle lies in your face

Now, this is a little grudge of mine against Gradle's marketing habits. When going on their website, they will feature a "Gradle vs Maven" comparison claiming that Gradle is "oh so much faster" than Maven and the following picture.

pseudo-benchmark

Now, let's take a closer look...

First, what's shocking is that a "Clean build with tests" is so much faster than the original build! It's almost instant! Including tests! ...let me get this straight: this is not "clean" at all. It's just doing nothing. I find Maven much more sensible in that case, it actually rebuilds everything from scratch. To go a little bit further, a "build" in Maven will just check for changes and compile changed files, which would result in a similar figure, while a "clean build" will remove the whole directory and re-build everything. I find this should be the expected behavior unlike Gradle's "clean build" not cleaning anything. After all, the aim of a clean build is usually to fix issues due to some undesirable thing lying around in the build directory, for whatever reason.

Then, let's look at the normal case: is Gradle really twice as fast? Well, here is another question for you: who compiles the source code? ...got an idea? Well, it's the javac compiler from the JDK, it's not the build tool! So why would Gradle be twice as fast?! Here is the trick: Gradle runs the tests in parallel while Maven do it sequentially. That is the reason! Gradle ain't faster or anything, it just runs the tests in parallel. I dislike this default. It's just a question of time until you get tests having side-effects and race conditions. Then you'll obtain "Heisenberg tests" succeeding sometimes and failing sometimes, depending on how their executions overlap. You'll wonder why and waste lots of time investigating the issue. Moreover, it usually runs in a background jobs after commits anyway.

Now, while I dislike Gradle's defaults, what I'm really annoyed about is how they distort the truth. They should say "we run tests in parallel by default and our 'clean' does nothing instead!". That should have been the correct way to put it instead of using their misleading statements insinuating that they compile faster.

Gradle is not simple

For Maven, the scope of dependencies is relatively straightforward:

  • Compile (the "usual" dependency)

  • Test (for tests)

  • Provided (provided at runtime by JDK or a container)

  • Runtime (quite rare. For drivers or alike available at runtime but not for compiling)

It's enough and I never needed anything else.

Gradle on the other hand has lots of scopes:

  • api

  • implementation

  • compileOnly

  • compileOnlyApi

  • runtimeOnly

  • testImplementation

  • testCompileOnly

  • testRuntimeOnly

  • ...a few more deprecated scopes

  • ...a few more classpath scopes

  • ...you can also extend and combine scopes

Well, you basically get it. Gradle is "super-customizable", so much that you often wonder what it exactly does or that you make a mistake without realizing. Gradle sells it as "Maven has few, built-in dependency scopes, which forces awkward module architectures" but IMHO it's Gradle which is confusing and overcomplexified here while Maven has exactly what's actually required.

That is just the tip of the iceberg. But basically Gradle is super-customizable while maven favors conventions. No wonder Gradle is also a company that thrives with support and training. If it was simple, such things would not sell.

Gradle needs maven, but not the other way round

Every single library in the Maven Central Repository must have pom.xml. It's the declarative definition of the library containing name, version, license, etc, and most importantly the list of its dependencies. Without a Maven pom.xml there would be no Central Repository nor dependency management possible.

Whether you use Grade or Maven, both read the pom.xml Maven definition to build the dependency tree. It's at the core of the dependency dependencies system to pull all transitive dependencies and resolve version conflicts.

In other words, Maven can live without Gradle, but Gradle still needs Maven to exist. Maven just applies a standardized build based on the pom.xml while Gradle builds in in some way and generates a pom.xml as a build step if you want to actually publish your library.

Maven isn't perfect either

Now, I bashed a lot about Gradle, but Maven isn't perfect either. It has issues too. Their website sucks IMHO, it could welcome YAML as more compact alternative format, some plugins should be built-in and the format itself could be tweaked here and there. But overall I find it OK considering it's a format that lived more than 20 years.

The other drawback is a lack of flexibility. It's indeed rigid in how it expects your project to be and may become problematic if you need for example to mix multiple different techs. For example a building a node project, running a python script, etc. as part of the build procedure to place some extra stuff inside the produced artifact. But for that IMHO, it's better to use CI scripts, running as GitHub actions or GitLab pipelines to build a "mixed bundle". Let each tech stack build its own artifacts and combine them later through scripting. I favor that approach over pushing the build scripts customizations too far.

Take it with a grain of salt

While I bashed at Gradle and praised Maven, it should be taken with a grain of salt. At the end of the day, they are just tool and either can be used wisely or like a fool.

With maven too, you can also produce "monster pom.xml files" by using tons of plugins and super-complex configurations overriding all defaults. Likewise, Gradle is not necessarily a monster. Use it wisely, keep your build script clean, refrain from adding custom build steps and you will do just fine. It's not bad per-se.

It's just that by default, in the hands of average developers, Maven's pom.xml will tend to remain understandable (because it takes effort to escape out of the conventions) while Gradle's build.gradle will tend to become more complex and customized over time (because it's so easy to do so). All the small shortcuts now and little extra steps that stray away from the build conventions tend to become liabilities in the long term.

As said previously, Gradle's great flexibility and customizability of the build is both a boon and a curse. Although I prefer to build "generic" projects where I can, because it's by far simpler to maintain in the long run, using Gradle definitely has its place when you need more specific stuff that requires customization.

TL;DR: as a rule of thumb, Maven's pom.xmtends to remain fairly generic with time, while Gradle's build.gardle leans towards being highly customized and therefore complex. This is due to their "nature", while Maven is based on a rigid project "definition", Gradle is a free form build "script".

Top comments (8)

Collapse
 
khmarbaise profile image
Karl Heinz Marbaise • Edited

Can you please be more specific about Their website sucks IMHO,? file in a jira issue ? Suggest the issues you see or even greater would be to offer a pull request?

The next part: it could welcome YAML as more compact alternative format, while YAML has a lot of disadvantages compared to XML ...yes I know XML is a bit verbose...in Maven 4 there is done a lot of foundation work to improve the format later on...

Collapse
 
dagnelies profile image
Arnaud Dagnelies • Edited

First off, thanks for the feedback. I'm glad to see someone caring about maven.

Can you please be more specific about Their website sucks IMHO?

Well, IMHO it should start with a getting started section, explaining how a basic pom.xml looks like and building it with the command line. That would be a fair introduction. On the other hand, their front page contains either advanced or useless stuff. Just by looking at it, I think most people would go away. Putting something like this, (not trivial to find) on the front page would have been much more useful IMHO. Besides the front page with a tutorial, I'd offer 4 sections: "pom.xml reference", "CLI reference", "Plugins", "Advanced" and "Community / contributing". The current left menu is cluttered while not containing what you look for.

file in a jira issue ?

Well, that is the next problem. Nowadays everybody has GitHub accounts. Just by requiring people to create an account and use another ticketing system, it looses feedback and contributions. You don't want to create accounts for every lib or tool you use.

YAML has a lot of disadvantages compared to XML

While YAML is not perfect, I think a lot of users would welcome a more lightweight way of wrting the configs


The last thing I'd like to add is that some things are also uselessly difficult to do, like packaging a fat/executable jar. Some comfort stuff like a goal to check for CVEs or update all dependency versions should also be included by default.

Collapse
 
grisha9 profile image
Grigoriy Myasoedov

While YAML is not perfect, I think a lot of users would welcome a more lightweight way of wrting the configs

There is polyglot-maven project that allows you to write build scripts in different languages. including Yaml

Thread Thread
 
dagnelies profile image
Arnaud Dagnelies

Yeah, I noticed that too. But it requires you to explicitly search for it, to download, install and configure the plugin. Basically, it's defying the purpose of making your life easier. Whether the plugin works properly and how your dev tools integrate it is another matter.

Collapse
 
rytheturtle profile image
RyTheTurtle

You can effectively implement your entire CD pipeline with Gradle. Maven, at least in my experience, is much more centered around just the build process (compile the code, run tests, bundle up your JAR/WAR).

In every team I've worked on so far, there is some external CD pipeline like Jenkins, GitHub Actions, Gitlab Pipelines, or something else that teams use to actually deploy software. Dedicated CD pipeline tools are very effective at managing and visualizing pipelines and stages, so the extra complexity of Gradle that enables it's fancy features just aren't a compelling selling point. So my opinion on Gradle vs Maven comes down to "doesn't really matter, just give me a command that can compile my code and run unit tests".

Collapse
 
eduardopazz profile image
Edu Paz

Totally agree with you

Collapse
 
gorynych profile image
Stepan Mozyra

When I see a Gradle project, I have no idea what the build script does

Oh, man! Same for me. When I see pom.xml - I have no idea what is going on.

Untill now I thought this is my problem, becouse I don't know Maven. But this artice helps me clearly understand that it's completele Maven's fault!

Collapse
 
dagnelies profile image
Arnaud Dagnelies

Huh?