This in-depth comparison helps you decide whether to use the Maven or Gradle build tool. Whatever your use case, you’ll find the answers here to quickly enable you to pick the right tool to support your project’s growth.
Overview
Both Maven and Gradle fall into the category of build tools. They’re designed to automate the work involved to take your application’s source code and transform it into an artifact to be published.
A developer could do this themselves, but it would be a tedious manual process involving:
-
compiling the code
-
packaging it up
-
publishing the final artifact
Build tools automate these processes and make building software faster and simpler.
Due to the requirements of modern applications, Maven and Gradle also do a lot more. Including running the application locally, ensuring tests pass, analysing the code, and much more.
In the rest of this article we’ll explore how Maven and Gradle can satisfy the requirements of a modern build tool. I write this article as an independent author and software engineer, and present what I believe to be a fair comparison.
Maven features
Maven was created in 2002 as a direct response to the frustrations of building Java applications using its predecessor, Apache Ant.
3 key improvements that Maven’s creator, Jason van Zyl, included are:
-
default directory structure in Java projects.: helps developers easily move between projects
-
standardise how Java applications are built: the default Maven lifecycle consists of a series of phases (e.g. compile, test, and package). Developers don’t have to rewrite build logic in new projects & can build projects on the Maven command line interface (CLI) in a standard way.
-
store dependencies externally: dependencies move out of version control and into a remote repository known as Maven Central. This makes updating dependency versions simpler.
Maven’s timeline
Maven runs within the Java Virtual Machine (JVM) and it’s core functionality builds Java applications only. Using 3rd party plugins like the kotlin-maven-plugin you can build Kotlin projects, the scala-maven-plugin for Scala projects, and other language plugins exist too.
Maven projects are defined with an XML build file containing at a basic level:
-
the project group, artifact id, and version
-
what plugins to apply
-
the project’s dependencies
You’ll see a Maven build file example later, but once created you can interact with the project via the Maven CLI.
For example, running mvn test
executes the default Maven lifecycle, including all phases up to and including test.
-
validate: ensures the Maven project is correct
-
compile: transforms Java source code into bytecode
-
test: executes tests to verify application functionality
(some phases omitted for clarity)
Each phase has goals attached, which correspond to code that actually does the work. e.g. the compile phase contains the compiler:compile goal. You’ll learn more about how phases and goals work later.
Finally, Maven is a free-to-use open source project whose source code is available on GitHub. It’s part of the Apache software foundation, which supports open-source software with help from donations
Gradle features
Gradle was created in 2008 to solve some of the shortcomings of building projects with Maven.
Gradle’s founder, Hans Dockter, took inspiration from Maven but had a vision for a more modern build tool. The main features he added included:
-
code-based build script to replace XML build file: using the Groovy language (and now Kotlin) makes defining build configurations less verbose than using lengthy XML tags
-
easily customisable build model: this is modified directly from the build script. Add custom build logic on the fly without having to develop a separate plugin (see Maven vs. Gradle customisation comparison below).
-
incremental build and other performance enhancements: makes building applications much faster, especially when making minor code changes that don’t require a full rebuild (see Gradle vs. Maven performance comparison below).
Gradle’s timeline
Gradle also runs inside the JVM. Out-of-the-box it supports building Java, Groovy, Scala, and even C++ applications. 3rd party plugins can be used for other languages like Kotlin.
With Gradle you decide if you want to write build scripts in Groovy or Kotlin. There are lots of similarities between the 2 languages.
The build scripts contain configuration such as:
-
the project’s group, name, and version
-
the plugins to apply
-
dependencies of the application
In Gradle a task is a unit of work to get done in your build. It’s the equivalent of a Maven goal.
Once the build script is created, you can interact with Gradle via the Gradle CLI. Running ./gradlew test
executes all tasks required to test your application, including:
-
classes: compiles code and copies resources
-
testClasses: compiles all test code and copies test resources
-
test: actually runs the tests themselves
(some tasks omitted for clarity)
Gradle models all the tasks in your project in a task graph, where a task can have dependencies on other tasks. Gradle uses this to ensure each task executes at most once. If the task has been executed in a previous build and nothing has changed, Gradle saves time by not executing it again.
The Gradle build tool is an open-source free-to-use product, distributed under the Apache License. It’s maintained by Gradle Inc., who also offer other products and services as part of their paid Gradle Enterprise service.
Maven vs. Gradle performance comparison
Gradle introduced several performance optimisations missing from Maven to improve build performance.
To see what difference they make, we’ll compare Maven vs. Gradle performance in these scenarios.
-
clean, then full build with tests: simulates build on fresh code checkout or CI server
-
build with tests without clean: simulates rebuild with no changes
-
small code change, then build with tests: simulates rebuild after developer makes a change
Each scenario was tested across the 2 project types below. An average was taken of 3 results.
Where the Gradle build task is shown below, the package phase was used in Maven.
Small Java project
10 subprojects each with 50 main classes and 50 test classes. 1,000 class total.
clean build (seconds) | build (seconds) | Code change then build (seconds) | |
---|---|---|---|
Maven | 43 | 17 | 18 |
Gradle | 25 | 1 | 4 |
Gradle improvement | 42% | 96% | 77% |
Medium Java project
100 subprojects each with 100 main classes and 100 test classes. 20,000 class total.
clean build (seconds) | build (seconds) | Code change then build (seconds) | |
---|---|---|---|
Maven | 543 | 175 | 171 |
Gradle | 340 | 3 | 12 |
Gradle improvement | 37% | 98% | 93% |
Summary of performance results
We see that in every scenario Gradle has improved performance over Maven from 37% to 98%.
This remarkable difference is thanks to the following Gradle features:
-
incremental build: avoids repeated class compilation or test runs
-
Gradle daemon: continuously runs Gradle in the background ready to execute a build
-
incremental compilation: Gradle analyses sources and classes, recompiling only if necessary
Note that both Maven and Gradle support parallel build, which improves performance by doing work on multiple threads at the same time. These features are by default disabled, so were not used in the above performance testing.
Maven vs. Gradle code comparison
Both Maven and Gradle require a build file to configure a project.
To compare how they look, below is Maven’s pom.xml and Gradle’s Groovy build.gradle for a simple Java Spring Boot application (click to toggle).
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.tomgregory</groupId>
<artifactId>spring-boot-api-example</artifactId>
<version>0.1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.1</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.200</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.22.2</version>
</plugin>
</plugins>
</build>
</project>
plugins {
id 'java'
id 'io.spring.dependency-management' version "1.0.11.RELEASE"
id 'org.springframework.boot' version '2.6.1'
}
group = 'com.tomgregory'
version = '0.1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-devtools'
implementation 'com.h2database:h2:1.4.200'
compileOnly 'org.projectlombok:lombok:1.18.22'
annotationProcessor 'org.projectlombok:lombok:1.18.22'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
test {
useJUnitPlatform()
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
Maven’s pom.xml is 62 lines, so almost twice the length of the 35 line build.gradle. This difference is mainly because XML is just more wordy.
With such a large size difference, for many the build.gradle is easier to read and more maintainable than the pom.xml.
For example, in Gradle a pull request to add a new dependency is a 1 rather than 4 line change.
</dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-actuator</artifactId>
+ </dependency>
<dependency>
implementation 'org.springframework.boot:spring-boot-starter-validation'
+ implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
Gradle Kotlin build scripts
The above build.gradle script uses the Groovy language, which was the first language supported by Gradle. Now Gradle build scripts can also be written in Kotlin.
The code below shows how to apply plugins in Kotlin and Groovy. The code differences are minimal.
plugins {
java
id("io.spring.dependency-management") version "1.0.11.RELEASE"
id("org.springframework.boot") version "2.6.1"
}
plugins {
id 'java'
id 'io.spring.dependency-management' version "1.0.11.RELEASE"
id 'org.springframework.boot' version '2.6.1'
}
To learn why to use Kotlin rather than Groovy with Gradle, check out 5 reasons to switch to the Gradle Kotlin DSL.
Project structure
Unlike Maven, Gradle requires several other files and directories to be added to your repository in addition to build.gradle.
-
gradle directory contains the wrapper code and properties (more on the Gradle wrapper shortly)
-
gradlew & gradlew.bat scripts for running a Gradle build via the wrapper
-
settings.gradle contains extra project settings like the project name
(all files can all be generated automatically)
The image below shows the directory structure of a real Maven/Gradle project.
Note that the directory structure of the code itself, such as putting main code in src/main/java, is consistent between Maven and Gradle.
Maven vs. Gradle usability comparison
To build applications with Maven a local Maven installation is required, which Gradle avoids through its wrapper script. The Gradle wrapper means that engineers can clone a repository and immediately build the project without installing Gradle. This also makes Gradle easier to use on CI servers.
Once everything is setup, to run a Maven build you pass a phase to the Maven command (e.g. mvn package
). With Gradle you pass a task to the Gradle wrapper (e.g. ./gradlew build
).
Maven creates build artifacts within a target directory, whereas Gradle uses a build directory.
The console output for Maven shows all info level log statements and test output. Gradle has a dynamic console, which shows only the task it’s currently working on with a final success/failure message.
Which console is better is subjective, but some may find Gradle’s console easier to read as it’s more concise. If detailed output is needed, you can enable it via the command line options.
For ongoing support with Maven it’s not obvious where to go. You cannot raise issues on the GitHub repository and the Slack channel is only for contributors.
For Gradle support you can ask questions on the Gradle forum, Gradle community Slack channel, or raise issues on the Gradle repository itself. You can also debug Gradle build scripts directly from your IDE to solve issues yourself.
Maven vs. Gradle customisation comparison
Once your project is setup to compile your application and run the tests, you might want to add more functionality. Both Maven and Gradle offer plugins to, for example, run static code analysis on calculate test code coverage.
What about further customisation though?
To customise a Maven build you only have 1 option, which is to write your own plugin. Writing custom plugins means creating a separate project and publishing the plugin artifact to be used in any project.
With Gradle there are 3 levels of customisation.
-
build script: define tasks, methods, and classes to reuse within the build script
-
project: put code in the special buildSrc directory to reuse across multiple subprojects
-
cross-project: create a separate project and publish a reusable plugin, as with Maven
Customisation example
Gradle’s build script, coupled with its dynamic build model, allows more customisation than Maven. For example, we can easily register a task to print the dependency count.
tasks.register('countDependencies') {
doLast {
println "Dependency count: ${configurations.getByName('compileClasspath').getAllDependencies().size()}"
}
}
Then execute the task.
$ ./gradlew countDependencies
> Task :countDependencies
Dependency count: 6
BUILD SUCCESSFUL in 1s
1 actionable task: 1 executed
To add similar custom behaviour in Maven involves creating an entire separate project.
Being able to reuse build logic so easily in Gradle is important to reduce duplication and improve maintainability, especially for larger multi-module projects.
Finally, both Maven and Gradle support multi-module projects for when your projects grows in size. In Maven the sub-modules are defined in the pom.xml build file itself whereas in Gradle they go in the settings.gradle file.
Choosing between Maven & Gradle
Now you know the differences between Maven and Gradle, maybe you already have a preference?
If not, here are some further recommendations based on your situation.
Using Gradle on a brand new project
For a brand new project use Gradle instead of Maven, because it’s:
-
faster
-
more concise
-
easier to customise
-
promotes build code reuse
-
improves the developer experience
In summary, Gradle increases developer productivity allowing businesses to more effectively add value to their customers.
If you want to use Gradle for your next project and don’t know where to start, I recommend my extremely helpful all-in-one Gradle Hero course. It helps you master building Java applications with Gradle as quickly as possible.
Considering a Maven to Gradle migration
For a Maven to Gradle migration there are some other factors to consider.
-
resourcing: Maven is more universally known than Gradle, so your team may take some time to get up to speed
-
timeline: migrating projects takes time, so consider the lifespan of your application and the frequency you change it
-
futureproof: Gradle is becoming more popular, has more active development, and has more modern features than Maven
For an application that sees regular development, I recommend a migration to Gradle to save time for developers. If you consider all the development hours spent waiting for Maven builds to complete, suddenly the time investment to perform the migration can seem insignificant.
Migrating your project might be quicker than you think. To learn about the steps involved see How to do a Maven to Gradle migration on a Java Spring Boot project.
For applications that see very infrequent development (e.g. less than a few changes per year), sticking with Maven may still be the best option.
For some inspiration, here’s the experience of the Spring Boot team who migrated from Maven to Gradle in early 2020.
Migrating the build to Gradle has undoubtedly been a success. As noted above, a full Maven-based build was taking an hour or more, both on CI and on developers’ own machines. Over the last four weeks, the mean successful build time with Gradle has been 9 minutes 22 seconds,
Andy Wilkinson, Migrating Spring Boot’s Build to Gradle
Your team and Gradle
If you know you want to migrate to Gradle, but need to convince your team, here are some benefits from different team-members’ perspectives.
Side-by-side feature comparison
Maven | Gradle | |
---|---|---|
Builds Java applications | ✔️ | ✔️ |
Builds other languages with plugins | ✔️ | ✔️ |
Builds other languages without plugins | ❌ | ✔️ |
Performance | ||
Parallel build | ✔️ | ✔️ |
Incremental build | ❌ | ✔️ |
Incremental compilation | ❌ | ✔️ |
Runs in background as daemon | ❌ | ✔️ |
Usability | ||
No installation required | ❌ | ✔️ |
Dynamic console | ❌ | ✔️ |
Customisation | ||
Supports multiple modules | ✔️ | ✔️ |
Supports 3rd party plugins | ✔️ | ✔️ |
Supports custom plugins | ✔️ | ✔️ |
Supports dynamic build logic | ❌ | ✔️ |
Technical | ||
Required execution Java version | 8+ | 8+ |
Supported compilation Java version | Any | 6+ |
Build file format | XML | Groovy/Kotlin |
v1 release | 2004 | 2012 |
Official website | maven.apache.org | gradle.org |
Maven vs. Gradle popularity
Popularity is not a measure of how good a tool is, but for interest usage data is shown below.
Build tool usage in 2021
Gradle monthly downloads
Search popularity in 2021
The above data shows that Maven and Gradle are both popular build tools. But Maven has significantly more usage than Gradle.
Summary
Both Maven and Gradle are tools to build modern Java applications. This article has highlighted the key differences between them in terms of performance, code, usability, and customisation.
The comparisons indicate that Gradle is now the better build tool for increased developer productivity. Maven still has some use cases where it may be more suitable (see Choosing between Maven and Gradle and the comparison table), but in general Gradle is the best choice for both small and large projects.
To continue learning Gradle check out one of these resources, depending how deep you want to go:
- to get your feet wet with some code right away, head over to Gradle tutorial for complete beginners
- for a more in-depth introduction, sign up to my free course Get Going with Gradle
- if you’re committed to mastering Gradle, then join my flagship Gradle Hero course 🚀
If you think there’s some aspect missing from this article, or you just have more questions, you’re welcome to email me on tom@tomgregory.com.
Stop reading Gradle articles like these
This article helps you fix a specific problem, but it doesn't teach you the Gradle fundamentals you need to actually help your team succeed.
Instead, follow a step-by-step process that makes getting started with Gradle easy.