Build Tools — Maven vs Gradle in 2026¶
Every Java project has a build tool. In 2026, there are still two credible options, and the choice is more political than technical. Maven is verbose but universal. Gradle is concise but complex. This file gives you the honest verdict, sets up both, and shows you the anatomy of each config file.
The 2026 Verdict¶
Default for enterprise Spring Boot / server-side Java: Maven. Predictable, XML-only, no Groovy/Kotlin DSL surprises.
Default for Android + polyglot + complex build logic: Gradle. Kotlin DSL is now stable; Groovy DSL is being phased out.
What Zoho likely uses: Maven. Almost all enterprise Java shops in India stick with Maven for backend services.
What OSS Java projects use: ~65% Maven / 35% Gradle in 2026 (rough estimate from GitHub topic search).
Learn Maven first. Add Gradle later if a project needs it.
The Comparison¶
Dimension |
Maven |
Gradle |
|---|---|---|
Config format |
|
|
Config lines |
100-500 for typical Spring Boot |
20-100 (concise) |
Learning curve |
Low — declarative, no logic |
Medium-high — full programming language |
Build speed |
Slower baseline; parallel + |
Faster with build cache + daemon |
Incremental builds |
Basic (Maven Reactor) |
Excellent (task-level incremental) |
Dependency resolution |
Predictable, closest-wins |
Rule-based, sometimes surprising |
Plugin ecosystem |
Massive (Maven Central plugins) |
Massive (Gradle plugin portal) |
IDE support |
Best in class (all IDEs) |
Best in class (all IDEs) |
CI/CD friendliness |
Trivial |
Trivial |
Debuggability |
Easy — read XML top-to-bottom |
Harder — logic can be anywhere |
Enterprise adoption |
Overwhelming |
Growing but slower |
Maven — What You Actually Use¶
Anatomy of a Spring Boot pom.xml¶
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<!-- Parent: inherits Spring Boot defaults (dep management, plugins) -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.0</version>
<relativePath/>
</parent>
<groupId>com.raghul.example</groupId>
<artifactId>petstore-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<java.version>21</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<version>1.20.4</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
The Maven commands you use daily¶
./mvnw clean install # Clean + build + test + install to local ~/.m2
./mvnw clean package # Clean + build + test + create JAR/WAR
./mvnw test # Run tests only
./mvnw -Dtest=UserServiceTest test # Run one test class
./mvnw -Dtest=UserServiceTest#shouldSave test # Run one test method
./mvnw dependency:tree # Show transitive deps
./mvnw dependency:analyze # Find unused / undeclared deps
./mvnw versions:display-dependency-updates # Check for newer versions
./mvnw versions:display-plugin-updates
./mvnw spring-boot:run # Run Spring Boot app (dev)
./mvnw -T 1C clean install # Parallel build, 1 thread per core
./mvnw -DskipTests package # Skip tests (CI/emergency only)
~/.m2/settings.xml — the personal Maven config¶
Location: ~/.m2/settings.xml. Common uses:
Corporate proxy config for Zoho’s network
Alternate Maven Central mirror (JFrog / Nexus)
Server credentials for private repos
Custom local repository path
<settings>
<mirrors>
<mirror>
<id>zoho-nexus</id>
<mirrorOf>*</mirrorOf>
<url>https://nexus.internal.zoho/repository/maven-public/</url>
</mirror>
</mirrors>
<servers>
<server>
<id>zoho-nexus</id>
<username>${env.NEXUS_USER}</username>
<password>${env.NEXUS_PASS}</password>
</server>
</servers>
</settings>
Note: Zoho’s actual Nexus URL will differ; ask your platform team.
Maven Wrapper (mvnw) — always commit this¶
Every new project should ship a Maven Wrapper. It pins the Maven version per repo so no one has to install Maven globally:
mvn -N wrapper:wrapper -Dmaven=3.9.9
git add mvnw mvnw.cmd .mvn
Commit .mvn/wrapper/maven-wrapper.properties and .mvn/wrapper/MavenWrapperDownloader.java. Never commit ~/.m2/repository.
Gradle — When You Have to Use It¶
Anatomy of a Spring Boot build.gradle.kts¶
plugins {
java
id("org.springframework.boot") version "3.4.0"
id("io.spring.dependency-management") version "1.1.6"
}
group = "com.raghul.example"
version = "0.0.1-SNAPSHOT"
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
repositories {
mavenCentral()
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
runtimeOnly("org.postgresql:postgresql")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.testcontainers:postgresql:1.20.4")
}
tasks.test {
useJUnitPlatform()
}
The Gradle commands you use daily¶
./gradlew build # Clean + compile + test + assemble
./gradlew test # Test only
./gradlew bootRun # Run Spring Boot app
./gradlew dependencies # Dep tree
./gradlew --refresh-dependencies build # Force redownload
./gradlew clean # Clean build/ dir
./gradlew --scan build # Detailed HTML build report (uploads to Gradle Enterprise)
./gradlew bootBuildImage # Build a Docker image via Paketo buildpacks
Gradle Wrapper — always commit this¶
gradle wrapper --gradle-version 8.11
git add gradlew gradlew.bat gradle/
The Real Differences That Matter¶
Thing |
Maven |
Gradle |
|---|---|---|
First |
30s-2min |
20s-1min (with daemon) |
Incremental rebuild after 1 file change |
5-10s |
1-3s |
Reading someone else’s |
Easy |
Sometimes hard |
Complex conditional logic |
Painful (profiles) |
Native (it’s Kotlin) |
Company policy compliance in India MNCs |
Universally supported |
Often supported |
Community help (Stack Overflow) |
Massive |
Massive |
What NOT to Do¶
Do NOT install Maven or Gradle globally. Use wrappers (
mvnw/gradlew) in every project. Version drift is the #1 build support ticket.Do NOT mix Maven and Gradle in the same project. Pick one.
Do NOT commit
target/orbuild/. Both are output directories.Do NOT commit
~/.m2/. That’s your local cache.Do NOT skip tests in CI.
-DskipTestsis for local iteration only.Do NOT use Ant. It’s 2026. Ant is a historical artifact.
The Plan for This 13-Month Roadmap¶
Month |
Focus |
|---|---|
M1 |
Install both via SDKMAN. Create one hello-world project with Maven. |
M2-M6 |
Use Maven exclusively. Read |
M7 |
Spring Boot with Maven. Learn Maven profiles for |
M8 |
Try Gradle for one microservice in your portfolio to feel the difference. |
M9-M11 |
Use whichever fits the project. Default Maven. |
M12 |
study prep: be able to explain Maven lifecycle phases ( |
Publishing to Internal Nexus / Artifactory (Zoho scenario)¶
Every mid-large Java org runs an internal Nexus (Sonatype) or Artifactory (JFrog). To publish:
Maven:
<distributionManagement>
<repository>
<id>zoho-nexus-releases</id>
<url>https://nexus.internal.zoho/repository/maven-releases/</url>
</repository>
<snapshotRepository>
<id>zoho-nexus-snapshots</id>
<url>https://nexus.internal.zoho/repository/maven-snapshots/</url>
</snapshotRepository>
</distributionManagement>
Then ./mvnw deploy. Credentials in ~/.m2/settings.xml.
Ask your platform team for the actual URL and credentials pattern. Don’t hard-code passwords.
Return to README.md · Next: 04_git_and_workflow.md