Part 1: Boring Maven

January 29, 2021, by Christian Klinger, Lesezeit 4 Minuten
Now that we all understand that boring can be good, I would like to present one of my personal favorites. Because, let's face it, which tool could be more boring than Maven?

As I have laid out in Part 0: About 'Boring Technology', there are a few properties that make a technology a 'Boring Technology'. A prime example of such a technology is - in my opinion - the build tool Maven.

Explaining what it does is fairly easy. Explaining how to use it (mvn verify) is almost ridiculously simple (for ~90% of the use cases). Replacing it with another build tool is, depending on the plugins you're using, not trivial, but in general feasible.

Industry adoption is great, plenty of projects use Maven as their build tool and most Java developers have experience with it. Given that Maven is around 18 years old and maintained by the Apache foundation, I think it's safe to say that it is future-proof.

Maven is opinionated, this is not a tutorial, but I want to highlight a few key features that make Maven a good choice for you.

The POM

With Maven, you define the dependencies of your project, how your project is built within the so-called Project Object Model, usually written in a file named pom.xml. Yes, it's XML, it looks clunky and has a lot of visual noise. But, I'm going to argue the point that the POM is Maven's killer feature. This is a hot take and I'm going to have to explain this one thoroughly, so please bear with me.

Let's take a competitor, Gradle as an example. In Gradle, your build definition is written in either Groovy or Kotlin which makes it tempting to over-engineer your build process, been there, done that. Gradle also allows you to build an arbitrarily complex graph of interdependent tasks. Contrasting to that, the structure of a Maven build with its predefined lifecycle seems very limiting.

In practice though, it's totally sufficient and it makes all Maven projects have a very similar structure. When thinking about the design space and the trade-offs between a full-blown programming language and a restricted structure one might want to check out what the new kids on the block are doing: Rust's Cargo.toml, Haskell's cabal-File, NPM's package.json. What do all of these build tools have in common? They have a very restricted structure.

In other words, Maven is strongly opinionated when it comes to how you should structure your project, making it easy for people joining your project navigate and understand your build process.

By the way, if it's only the XML syntax that bothers you, there's takari/polyglot-maven which allows you to write your POM in different syntactic flavors, e.g. YAML.

So, now after talking about the anti-features Maven doesn't have, let's talk about the features Maven does have. In my opinion, Maven shines when you have more than one project or modules that are connected because Maven has plenty of features to structure these. Let's have a look!

(Note: If you're looking for a tutorial, check out Maven in 5 Minutes, and if you're looking for more in-depth information, there's the comprehensive POM reference

Archetypes

Maven's archetypes are basically blueprints to create new projects. If you're developing Javascript, then you might know Yeoman which is basically the same idea. It's also very easy to add new templates as they're just regular Maven artifacts which you can, for example, upload to Maven Central or your local Artifactory. To create a new project using an artifact (here: maven-archetype-quickstart) just run

mvn archetype:generate -DarchetypeArtifactId=maven-archetype-quickstart.

Parent POMs

When you have multiple projects that are similarly structured, it can make sense to factor out the commonality into a new project and define that project as the parent of the projects. This is very similar to Java's single inheritance model. This feature is powerful, but exactly as with Java's inheritance, it should be applied judiciously.

Good to know: Every project ultimately inherits from the Maven Super POM.

Another tip: Often, Parent POMs are used to centralize the version constraints (which can usually be found in the "<dependencyManagement>" section). A better way to manage dependencies is to use a separate module of type "pom" that declares these dependencies and import the versions from there (using dependency scope "import"). This helps you to avoid your parent POM becoming a 'kitchen sink', listing every dependency any sub-project ever needed.

Aggregate Projects

You can break down a project into multiple modules joined by an aggregate POM. Building this aggregate project automatically builds the required modules -- even if these depend on each other as Maven will determine the right build order.

Nice: Parent POMs and Aggregate projects can be combined.

Conclusion

Maven is nice, so use it and stop thinking about it.

Feedback, Comments? Discuss with us on Twitter @sparkteams.