Thoughts on Apache Maven

At this point in time, the Java ecosystem has 2 main build tools: Maven and Gradle.
Big Java projects such as Google's Android and the Spring Boot project started relying on Gradle for their builds but I'm pretty sure that many companies are still happily using Maven.
My own urge to switch to Gradle is quite strong but I think that it is smarter to stick with Maven for some more time to understand the "essence" of what a build tool is and the problem it is supposed to solve. These fundamentals are shared by most build tools in most languages so switching from one tool to the next before having understood them makes little sense. The project I am on happens to be using Maven so it makes sense for me to understand the intricacies of a build tool with Maven. Once that is done, I can experiment with any other build tool as I please. The core concepts are the same, I am able to focus on the implementation details and ergonomics of the various build tools instead.
What are my thoughts on Maven? I think it gets the job done, is verbose, and has poor discoverability of functionality.
Maven gets the job done
Basic use of Maven can be figured out quickly. https://mvnrepository.com/ can be used to find the dependencies you need, it even provides the code snippets needed to add them to your pom.xml
. Basic workflows only require the commands mvn clean
, mvn test
, mvn compile
, mvn install
and mvn sprin-boot:run
. The integration with IntelliJ is great so there is little friction after the initial setup.
Maven is verbose
Having said that, Maven is very verbose in comparison to other build tools. The primary culprit here is the XML format.
The specification of dependencies takes up at least 5 lines whereas Gradle only needs 1 line. Why? In Maven the groupId
, artifactId
and version
are all separate XML elements but Gradle just parses a string with separators to figure out this information.
Maven's pom.xml
also starts with a lot of boilerplate code at the top of the file
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
Omitting most of this information does not seem to affect the tool but official docs and auto-generated pom.xml
s have it by default. I do not understand why this information cannot be hidden by default and overridden on-demand instead.
The same pain-point is also present when configuring plugins, for example the JaCoCo plugin (Java Code Coverage tool) for Maven needs at least 2 execution
elements specified. The prepare-agent
execution, and then the executions for the reports that you need generated. Without the prepare-agent
the setup does not work, but by default the prepare-agent
does not need any configuration, it just needs to be declared like so:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.13</version>
<executions>
<execution>
<id>prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
This is just pure ceremony, does it really have to be this way?
The explanation seems to be that Maven's philosophy is to prefer explicit over implicit configuration. So, if you want something to be part of your build you have to actively put it there – so far so good. In the case of JaCoCo, the code execution needs to be altered by attaching the JaCoCo agent. The plugin people felt this was too invasive of a thing to default to and that it should be actively opted into by the plugin user.
This is where it stops making sense to me. The report does not work without the prepare-agent
step, I am forced to specify it every time. It seems much less intuitive to have the report step secretly fail like this because I forgot to declare the agent preparation stage.
What's the alternative? The prepare-agent
step done automatically when you add report goals and can be overridden on-demand by the plugin user, if needed.
Maven functionality is hard to discover
I found it hard to navigate Maven functionality. How to spin up a maven project from the command line? Apparently you do it like this: mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=my-app -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.5 -DinteractiveMode=false
.
This is already insane as it is, but the most frustrating part for me is that there is no apparent way to figure this out from the command line, you have to google for it.
Is this a necessary evil that we have no choice but to accept? I don't think so, in Gradle it is possible to run gradle help
and it will guide you towards the right command, which is gradle init
. It then guides you through the initialization process. But maybe you don't know about gradle help
, it does not adhere to the standard way of displaying helpful docs in the terminal. If you run gradle --help
and scroll to the top of the outputs it points you to the existence of gradle help
and from there you end up with the desired result.
Conclusion
Even though I do not like the technical details of Maven I will stick with it for a bit longer in order to get a deeper understanding of the responsibilities of a build tool and the ways it is supposed to accomplish things.
I think that Maven gets one thing right compared to other build tools. It is declarative-first, XML is not a scripting language and this makes it harder to go crazy in your build configuration (you need to write plugins in Java and publish them to a Maven repository – that's a lot of friction). This makes builds more predictable and easy to understand. You could say that it is fine to trust developers with the sharp tool of scripting capability in the build tool and I would agree, but just like in sport, art or video games, it is better to learn in a more controlled environment before you start experimenting more freely.
... This makes Maven feel like a tutorial section of a video game, I promise I meant no harm with my analogy. Wrapping up, I'm sticking with Maven since I am focusing on learning the central concepts of a build tool and hopping from one tool to the next is not helpful for that. I'd also rather get comfortable with a more declarative approach first and then use this approach pragmatically in more permissive tools.