The pom.xml File

7 min

Table of contents


1 — The pom.xml, heart of the project

The pom.xml (Project Object Model) is the central file of every Maven project. It is an XML document that describes the project: its identity, its dependencies, its properties, and how to build it.

Every pom.xml starts with a <project> root and its namespace, and must declare the model version:

xml
<?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>

    <!-- The rest of the configuration here -->

</project>

<modelVersion>4.0.0</modelVersion> is mandatory and is always 4.0.0 today: it is the version of the pom.xml file format, not the version of your project.

↑ Back to top


2 — Overall structure of the pom.xml

A pom.xml is read from top to bottom, in large blocks. Here is the typical organization:

BlockRole
<modelVersion>Format version (always 4.0.0)
<groupId> / <artifactId> / <version>Coordinates (project identity)
<packaging>Artifact type (jar, war, pom)
<properties>Reusable variables (versions, encoding)
<dependencies>Libraries in use (lesson 03)
<build>Plugins and build configuration

The <packaging> block determines the deliverable type:

ValueProduct
jarJava library or application (default)
warWeb application deployable on a server
pom“Parent” project with no code (aggregator)

If you omit <packaging>, Maven chooses jar by default. Once again: convention over configuration.

🔧 Mini-exercise — Write the <packaging> tag that produces a web application deployable on Tomcat.

✅ See a solution
xml
<packaging>war</packaging>

↑ Back to top


3 — Project coordinates (GAV)

Every Maven project is identified uniquely by three coordinates, often abbreviated GAV: GroupId, ArtifactId, Version.

xml
<groupId>com.exemple</groupId>
<artifactId>mon-app</artifactId>
<version>1.0.0</version>
CoordinateMeaningConvention
groupIdThe organization / the projectReversed domain name: com.exemple
artifactIdThe module nameShort, lowercase: mon-app
versionThe current versionNumeric: 1.0.0, or 1.0.0-SNAPSHOT

The -SNAPSHOT suffix deserves special attention:

VersionMeaning
1.0.0-SNAPSHOTVersion in development, unstable, updated frequently
1.0.0Published version (release), frozen and immutable
bash
# The produced artifact name combines artifactId + version
# e.g. mon-app-1.0.0-SNAPSHOT.jar
mvn package

The combination groupId:artifactId:version is your project's “postal address” in the Maven ecosystem. That is how other projects will be able to depend on it.

🔧 Mini-exercise — Write the three GAV tags for a com.banque project named gestion-comptes in development version 1.0.0.

✅ See a solution
xml
<groupId>com.banque</groupId>
<artifactId>gestion-comptes</artifactId>
<version>1.0.0-SNAPSHOT</version>

↑ Back to top


4 — Properties

The <properties> block defines reusable variables throughout the pom.xml. You centralize versions and settings there to avoid repetition.

xml
<properties>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <junit.version>5.10.2</junit.version>
</properties>

You then reuse a property with the ${name} syntax:

xml
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>${junit.version}</version>   <!-- reuses the property -->
    <scope>test</scope>
</dependency>

Common properties:

PropertyRole
maven.compiler.source / targetJava version of the source code and the bytecode
project.build.sourceEncodingFile encoding (always UTF-8)
Custom properties (junit.version…)Centralize version numbers

Centralizing versions in <properties> is good practice: to move JUnit from 5.10 to 5.11, you change one place instead of every dependency.

🔧 Mini-exercise — Define a gson.version property set to 2.10.1, then show how to reuse it in a <version> tag.

✅ See a solution
xml
<properties>
    <gson.version>2.10.1</gson.version>
</properties>
<!-- ... -->
<version>${gson.version}</version>

↑ Back to top


5 — Parent inheritance

A pom.xml can inherit from another pom.xml via the <parent> block. The child then receives the parent's properties, dependencies, and configuration. This is the mechanism that lets you factor configuration shared by several modules.

xml
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.2.5</version>
    <relativePath/>   <!-- empty: look up the parent in the repository -->
</parent>

The best-known use is the Spring Boot parent, which pins the versions of dozens of libraries so they stay compatible with each other. Thanks to it, you declare dependencies without specifying their version:

xml
<!-- No <version>: it is inherited from the parent -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
With parent inheritanceWithout parent inheritance
Consistent versions guaranteedRisk of incompatible versions
Less repeated configurationEverything to redeclare in each module

The Spring Boot parent acts as a “validated shopping list”: it guarantees that all your libraries get along, without you having to pick every version number by hand.

↑ Back to top


6 — A complete commented pom.xml

Here is a realistic pom.xml that brings together everything above: coordinates, properties, optional parent, and dependencies.

xml
<?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">

    <!-- File format version -->
    <modelVersion>4.0.0</modelVersion>

    <!-- GAV coordinates: project identity -->
    <groupId>com.exemple</groupId>
    <artifactId>mon-app</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <!-- Reusable variables -->
    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <junit.version>5.10.2</junit.version>
    </properties>

    <!-- Libraries in use -->
    <dependencies>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

You can display the “effective” pom.xml (with all inheritance resolved):

bash
# Shows the complete pom, parent included
mvn help:effective-pom

The mvn help:effective-pom command is invaluable for understanding what Maven actually “sees”, including everything that comes from the parent.

🔧 Mini-exercise — Write the command that displays the effective pom.xml, with all parent inheritance resolved.

✅ See a solution
bash
mvn help:effective-pom

↑ Back to top


7 — Quiz — The pom.xml file

Question 1: What do the three GAV coordinates stand for?

a) Group, Action, Value

b) GroupId, ArtifactId, Version

c) Git, Apache, Version

d) General, Application, Version

See the solution

Answer: b) — GAV = groupId, artifactId, version: the project's unique identity.


Question 2: What does the -SNAPSHOT suffix mean in a version?

a) A published, frozen version

b) A version in development, unstable and updated frequently

c) A screenshot of the project

d) An obsolete version

See the solution

Answer: b)-SNAPSHOT marks a development version. A version without that suffix is a frozen release.


Question 3: What is the <properties> block for?

a) Declaring dependencies

b) Defining reusable variables (versions, encoding) with ${...}

c) Configuring the web server

d) Writing Java code

See the solution

Answer: b) — Properties centralize reusable values via the ${name} syntax, avoiding repetition.


Question 4: What is the benefit of inheriting from a <parent> such as spring-boot-starter-parent?

a) It removes the need for code

b) It guarantees consistent library versions and reduces configuration

c) It speeds up the network

d) It encrypts the pom.xml

See the solution

Answer: b) — The parent pins mutually compatible versions; you can then declare dependencies without specifying their version.


Question 5: What value does <modelVersion> always take today?

a) 1.0.0

b) 3.9.6

c) 4.0.0

d) 17

See the solution

Answer: c)<modelVersion>4.0.0</modelVersion> is the version of the pom.xml file format, mandatory and unchanging.

↑ Back to top


8 — Practice — Write a pom.xml

Instructions

Write a pom.xml from scratch for a project identified by com.banque:gestion-comptes:1.0.0-SNAPSHOT, packaged as a jar, compiled as Java 17 with UTF-8 encoding, and centralizing the JUnit version in a property. Then verify it.


Suggested correction — Expected pom.xml

xml
<?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.banque</groupId>
    <artifactId>gestion-comptes</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <junit.version>5.10.2</junit.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

Verification:

bash
# Check that the pom is valid and resolvable
mvn validate

# Display the effective pom (parent and properties resolved)
mvn help:effective-pom

Expected result:

[INFO] BUILD SUCCESS

If mvn validate prints BUILD SUCCESS, your pom.xml is syntactically correct and all its coordinates are well formed. A common mistake: forgetting <modelVersion> or leaving an XML tag unclosed.

↑ Back to top


9 — Summary

Key takeaways

  1. The pom.xml is an XML file that describes identity, properties, dependencies, and build.
  2. GAV coordinates (groupId, artifactId, version) identify the project uniquely.
  3. -SNAPSHOT = version in development; without the suffix = frozen published version.
  4. <properties> centralize reusable variables via ${name}.
  5. <parent> inheritance factors configuration and guarantees consistent versions.

What's next

Lesson 03 — Dependency management: declare libraries, understand scopes, repositories, and transitive resolution.

↑ Back to top


All rights reserved. Any reproduction, distribution, use or adaptation of this course, in whole or in part, is strictly prohibited without the prior written authorization of Dr. Haythem REHOUMA.

Course created by Dr. Haythem REHOUMA — Development and Deployment of Data Solutions