Understanding pom.xml
Every Maven project has a pom.xml file at its root. POM stands for Project Object Model, and it is the single source of truth for everything Maven needs to know about your project — what it is, what it depends on, and how to build it. This chapter breaks down the essential sections you will edit on a daily basis.
Prerequisites
- You have created at least one Maven project (by hand or with an archetype)
- Basic understanding of XML syntax
The Minimal POM
The smallest valid pom.xml looks like this:
<?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">
<!-- POM model version -->
<modelVersion>4.0.0</modelVersion>
<!-- Project coordinates -->
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- Packaging format -->
<packaging>jar</packaging>
</project>These five elements are the foundation. Without them, Maven refuses to build.
GAV Coordinates
Maven identifies every project and every dependency using three values collectively known as GAV:
| Element | Purpose | Example |
|---|---|---|
groupId | Identifies the organization or group that owns the project | com.example |
artifactId | The name of the project itself | demo |
version | The current release version | 1.0-SNAPSHOT |
Together, groupId:artifactId:version forms a unique address. Maven uses this address to locate artifacts in local and remote repositories.
groupId Naming Convention
Use your organization's reverse domain name:
| Organization | Domain | groupId |
|---|---|---|
| google.com | com.google | |
| Apache | apache.org | org.apache |
| Your personal blog | johndoe.dev | dev.johndoe |
If you do not own a domain, com.github.yourusername or io.github.yourusername are common community conventions.
artifactId Best Practices
- Use lowercase letters and hyphens:
my-library, notMyLibrary - Keep it short but descriptive
- It often matches the repository name on GitHub
Version Number Rules
A typical version looks like 1.2.3. The numbers mean:
| Position | Name | When to increment |
|---|---|---|
| 1 | MAJOR | Breaking API changes |
| 2 | MINOR | New features, backward compatible |
| 3 | PATCH | Bug fixes, backward compatible |
This is called Semantic Versioning (SemVer). Not every project follows it strictly, but it is the most widely understood convention in the JVM ecosystem.
SNAPSHOT Versions
A version ending in -SNAPSHOT marks a development build:
<!-- Active development version -->
<version>1.0-SNAPSHOT</version>Maven handles snapshots differently from releases. When a dependency is declared as a snapshot, Maven checks the remote repository for newer builds on every run. Once the code stabilizes, you remove the suffix:
<!-- Released stable version -->
<version>1.0.0</version>Tip
When to Use SNAPSHOT
Use -SNAPSHOT during active development. Switch to a release version only when you publish the artifact to a repository for others to consume.
modelVersion and packaging
modelVersion
<modelVersion>4.0.0</modelVersion>This tells Maven which version of the POM schema you are using. It has been 4.0.0 for over a decade. You will almost never change it.
packaging
<packaging>jar</packaging>The packaging element declares the output format. Common values:
| Value | Output | Typical Use |
|---|---|---|
jar | JAR file | Libraries and command-line applications |
war | WAR file | Java web applications deployed to Tomcat or Jetty |
pom | No artifact | Parent POMs and aggregator projects |
maven-plugin | JAR with plugin metadata | Custom Maven plugins |
If you omit packaging, Maven defaults to jar.
Properties
The <properties> block is a key-value store for values you reference elsewhere in the POM:
<properties>
<!-- Java source and target compatibility -->
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<!-- File encoding for source files -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- Custom property used later in dependencies -->
<junit.version>5.10.0</junit.version>
</properties>Properties are referenced with the ${...} syntax:
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>Tip
Why Use Properties?
Centralizing version numbers in <properties> makes upgrades painless. When JUnit releases a new version, you change one line instead of hunting through every <dependency> entry.
Project Metadata
Optional but recommended elements that describe your project to humans and tools:
<!-- Human-readable name -->
<name>My Demo Application</name>
<!-- Short description displayed in repository browsers -->
<description>A sample project for learning Maven.</description>
<!-- Project homepage URL -->
<url>https://github.com/example/demo</url>
<!-- License information -->
<licenses>
<license>
<name>MIT License</name>
<url>https://opensource.org/licenses/MIT</url>
</license>
</licenses>
<!-- Developer contact information -->
<developers>
<developer>
<name>Jane Doe</name>
<email>jane@example.com</email>
</developer>
</developers>These fields do not affect the build, but they appear in generated documentation and repository indexes.
Reading the Effective POM
The file you edit is only part of the story. Maven merges your POM with a built-in Super POM that defines defaults for repositories, plugin versions, and directory paths. To see the complete, merged configuration, run:
# Print the effective POM to the console
mvn help:effective-pomPipe it to a file if you want to inspect it carefully:
# Save the effective POM to a file
mvn help:effective-pom > effective-pom.xmlThis is invaluable when you are wondering why Maven behaves a certain way — the answer is often hidden in a default you did not know existed.
FAQ
Can I have multiple POM files in one project?
Not really. Maven looks for pom.xml in the project root. However, multi-module projects have a parent POM and one child POM per module. We will cover that in the Multi-Module Projects chapter.
What happens if I change the version after releasing?
Nothing breaks immediately, but it is confusing. Once an artifact with a specific GAV is published to a repository, it should never be overwritten. If you need to fix a bug, release a new version (1.0.1) instead of mutating 1.0.0.
Does the order of elements inside <properties> matter?
No. Properties are a simple map. Order them alphabetically or group them by concern — whatever makes your team happiest.
What is the difference between maven.compiler.source and maven.compiler.target?
source: the Java language features your code is allowed to usetarget: the JVM bytecode version that the compiler produces
For JDK 21, set both to 21. If you set target lower than source, the compiler may reject features that are valid in your source language level but unsupported by the target bytecode.
Can I use a different file name instead of pom.xml?
Only by using the -f flag on the command line: mvn -f my-pom.xml package. In normal practice, every project uses pom.xml and nothing else.