Skip to content
Ameen Abozaid
PublishedBEG-054-L04

The build lifecycle

  • Lesson 4 of 6
  • 10 min read
  • Beginner
On This Lesson 11 topics

Lesson Overview

You have a layout, a pom.xml, and dependencies. Now you will drive the build from the terminal. Maven does not have a different procedure for every command: it has one ordered list of steps called the build lifecycle, and each command means "go as far as this step".

In the recipe analogy, the card has numbered steps. Telling the assistant "stop after step 3" is mvn test; "do everything up to the finished dish" is mvn package.

Why this matters: the single most common source of confusion for Maven beginners is "why did that command also run the tests?" or "why do I have old files?". Once you know the lifecycle, those questions answer themselves. This lesson builds on lessons 2 and 3, and uses the example project (com.example:hello) with its JUnit tests from module BEG-045.

Learning Objectives

By the end of this lesson you will be able to:

  • List the main lifecycle phases in order: validate, compile, test, package, verify, install, deploy.
  • Explain that running a phase also runs every earlier phase.
  • Say what mvn compile, mvn test, and mvn package each produce.
  • Explain what mvn clean does and why it is a separate lifecycle.
  • Read Maven's output as a list of plugin goals tied to phases.
  • Skip tests deliberately and say when that is a bad idea.

Written Explanation

Phases in order

The default lifecycle is a fixed sequence of phases. These are the main ones (Maven's reference page lists a few more small ones between them):

PhaseWhat it means
validateCheck that the project is correct and has what it needs
compileCompile the main source code
testRun the unit tests against the compiled code
packageBundle the compiled code, for example into a JAR
verifyRun extra checks on the package (such as integration tests)
installCopy the package into your local repository so other local projects can use it
deployCopy the package to a remote repository for others (usually done by a server)

The rule that explains everything: running a phase runs every phase before it. mvn package is not "just package": it first validates, compiles, and tests.

Phases and plugin goals

A phase is only a name for a position in the sequence. The work is done by plugin goals attached to phases (lesson 2). For a JAR project the standard attachments are:

PhaseGoal that runs
process-resourcesresources:resources (copies files from src/main/resources)
compilecompiler:compile
process-test-resourcesresources:testResources
test-compilecompiler:testCompile (compiles the tests)
testsurefire:test (runs the tests)
packagejar:jar

You can watch it happen. This is the real output of mvn compile and mvn package on the example project, reduced to the goal lines:

text
$ mvn compile
[INFO] --- resources:3.3.1:resources (default-resources) @ hello ---
[INFO] --- compiler:3.13.0:compile (default-compile) @ hello ---

$ mvn package
[INFO] --- resources:3.3.1:resources (default-resources) @ hello ---
[INFO] --- compiler:3.13.0:compile (default-compile) @ hello ---
[INFO] --- resources:3.3.1:testResources (default-testResources) @ hello ---
[INFO] --- compiler:3.13.0:testCompile (default-testCompile) @ hello ---
[INFO] --- surefire:3.2.5:test (default-test) @ hello ---
[INFO] --- jar:3.4.1:jar (default-jar) @ hello ---

The package run is the compile run plus the test steps plus the jar step. That is the answer to "why does package run the tests": test is earlier in the list than package. (The finer steps process-resources and test-compile sit between the main phases; in the output they appear as goals.)

What each command produces

CommandDoesLeaves in target/
mvn compileCompiles main codeclasses/
mvn testAlso compiles and runs testsplus test-classes/ and surefire-reports/
mvn packageAlso builds the JARplus hello-1.0-SNAPSHOT.jar
mvn verifyAlso runs verification checksthe same files for this simple project

For our small project mvn verify ran exactly the same goals as mvn package, because nothing is attached to verify yet.

mvn clean: a separate lifecycle

clean does not belong to the default list. It is a tiny lifecycle of its own whose job is to remove what the previous build generated: the target/ folder. Real output:

text
$ mvn clean
[INFO] --- clean:3.2.0:clean (default-clean) @ hello ---
[INFO] Deleting C:\...\hello\target

Afterwards the target folder no longer exists. Because it is separate, you combine it with other phases on one line:

bash
mvn clean package

This deletes the old output and then builds everything from scratch, which is the most trustworthy build there is. Use it when something looks odd, for example a class you deleted still appears to exist.

Which command when

A short guide for daily work:

SituationCommand
"Does my code compile?"mvn compile
"Do my tests pass?" (the command you will use most)mvn test
"Give me the JAR, and prove it is sound"mvn clean package
"Something odd, maybe old files"mvn clean test
"What does my project depend on?"mvn dependency:tree

Each of these is one line you can type in the project folder, and each does the same thing on a teammate's computer. Running mvn test very often is normal: it runs your safety net quickly, so most developers run it after every small change. A full mvn clean package is for when you want a trustworthy result, for example before sharing your work. Notice that mvn compile and mvn test do not create the JAR, so anything that depends on the JAR (like running java -jar) needs package.

Phases versus goals

You can also run a single plugin goal directly, as mvn dependency:tree in lesson 3. That is the form plugin:goal. A phase (like package) runs a chain; a goal runs one task. The commands for this module use phases, with dependency:tree as the one useful extra.

Skipping tests

Maven can skip tests with a flag: mvn -DskipTests package. Real output:

text
[INFO] --- surefire:3.2.5:test (default-test) @ hello ---
[INFO] Tests are skipped.
[INFO] --- jar:3.4.1:jar (default-jar) @ hello ---

The test phase still ran, but did nothing. It is legitimate in rare cases, such as when a teammate asks for the JAR quickly, but building without tests removes the safety net: you could publish a broken program. Treat it as an exception, never a habit.

Where the analogy stops

A recipe's steps can be done in any order you like. Maven's order is fixed: you cannot ask for package and say "except compile". And a recipe has no "clean the kitchen" in its numbered list; in Maven it is a separate lifecycle you add yourself.

Examples

Example 1: what did mvn compile produce? (simple)

After mvn compile, the project has target/classes/com/example/Greeter.class. There is no test-classes folder and no JAR, because the build stopped after the compile phase.

Example 2: package runs the tests (practical)

mvn package on the example project printed (real output, tail):

text
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
[INFO] --- jar:3.4.1:jar (default-jar) @ hello ---
[INFO] Building jar: C:\...\hello\target\hello-1.0-SNAPSHOT.jar
[INFO] BUILD SUCCESS

Now we break a test on purpose (change an expected string). The build stops at the test step, and no new JAR is made:

text
[ERROR] Tests run: 2, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.144 s <<< FAILURE! -- in com.example.GreeterTest
[ERROR] com.example.GreeterTest.greetsByName -- Time elapsed: 0.078 s <<< FAILURE!
org.opentest4j.AssertionFailedError: expected: <Hi, Ana!> but was: <Hello, Ana!>
[INFO] BUILD FAILURE

Because test comes before package, a failing test prevents packaging. That is a feature: broken code should not become a deliverable. (In that run, target/hello-1.0-SNAPSHOT.jar was still present, but only because it was left from an earlier build. That is a reason to use clean.)

Example 3: a stale file and a clean build (realistic)

A teammate renames a class and builds. The old Greeter.class is still in target/classes and an old test is still passing against it. The fix is mvn clean package, so no file from a past build can hide a problem. On a real server the build always starts from a clean checkout, which is why "it passes on my machine but fails on the server" often traces back to stale output in target/.

Backend Insight

Build servers (continuous integration) run something like mvn clean verify or mvn clean package on every change. Because the same lifecycle runs everywhere, a red build on the server means exactly the same thing as a red build on your laptop. The later phases (install, deploy) are mostly about sharing artifacts: install puts a project into your local repository so another project on your machine can depend on it, and deploy publishes it to a shared repository. Beginners rarely run deploy; build servers do.

Common Mistakes

Mistake 1: Thinking mvn package skips the tests

It runs them first. If a test fails, no package is produced.

Mistake 2: Never cleaning

Stale files in target/ can make a broken project appear to work. When in doubt, run mvn clean package.

Mistake 3: Using -DskipTests as the default

A build without tests gives a false sense of safety. If a test fails, fix the test or the code.

Mistake 4: Running mvn clean and expecting it to build

mvn clean only deletes target/. It does not compile anything. Add a build phase: mvn clean package.

Mistake 5: Confusing phase and goal

mvn package is a phase and runs a chain. mvn dependency:tree is a goal and runs one task.

Exercises

Try each exercise yourself before opening the solutions.

Level A: Foundation

A1. Write the lifecycle phases validate, compile, test, package, verify, install, deploy in order and underline the three used most in this module.

Show solution A1

A1. validate, compile, test, package, verify, install, deploy; the three most used here are compile, test, package.

A2. What does mvn clean delete?

Show solution A2

A2. The target/ folder and everything in it.

A3. Which command compiles the main code only?

Show solution A3

A3. mvn compile.

Level B: Application

B1. After you run mvn test, which folders exist in target/ that were not there after mvn compile?

Show solution B1

B1. test-classes/ (compiled tests) and surefire-reports/ (test reports).

B2. A teammate says: "I only wanted the JAR, why did my build fail on a test?" Explain.

Show solution B2

B2. test runs before package, and a failing test stops the build, so no JAR is produced. Fix the test or the code; do not skip tests just to get the JAR.

B3. What is the difference between mvn package and mvn clean package?

Show solution B3

B3. mvn package reuses what is already in target/; mvn clean package deletes target/ first and builds everything from scratch.

Level C: Challenge

C1. On a scratch project, run mvn compile, then mvn test, then mvn package. After each, list the contents of target/ and write what was new.

Show solution C1

C1. After compile: classes/ (plus some bookkeeping folders). After test: also test-classes/ and surefire-reports/. After package: also <artifactId>-<version>.jar. Details vary slightly with plugin versions.

C2. Explain why mvn clean and mvn compile are separate lifecycles rather than one list.

Show solution C2

C2. clean removes old output; it is not a step towards a finished product. You often want to run it before a build, but not every time, so it is attached when you ask (mvn clean package) rather than being part of the default sequence.

Mini Challenge

On the project from lesson 2's Mini Challenge, run these commands one after another and write in one sentence what each added to target/: mvn clean, mvn compile, mvn test, mvn package. Then run mvn clean package. Finally break one test on purpose and run mvn clean package again. Note at which step the build stops, and restore the test. (If your project has no tests yet, copy the GreeterTest idea: one @Test that checks a method's result, and add JUnit as in lesson 3.)

Check your work: your notes match the table in this lesson, and the broken test stopped the build before the JAR step.

Summary & Next Lesson

Key takeaways

  • The default lifecycle is a fixed sequence: validate, compile, test, package, verify, install, deploy.
  • Running a phase runs all earlier phases, which is why mvn package runs the tests.
  • mvn compile builds classes; mvn test also runs tests; mvn package also builds the JAR.
  • mvn clean deletes target/; combine it as mvn clean package for a fresh build.
  • A failing test fails the build, which stops packaging.

Next lesson

The next lesson, "Running tests with Maven", zooms in on the test phase: which classes Maven treats as tests and how to read its summary.

Self-check

  1. Which phase comes first, package or test?
  2. What does mvn clean do?
  3. Does mvn package run the tests?
  4. What is the difference between a phase and a goal?
  5. Why avoid -DskipTests as a habit?
Show answers
  1. test.
  2. It deletes the target/ folder.
  3. Yes, test is an earlier phase and runs first.
  4. A phase is a step in the lifecycle that runs everything before it; a goal is one task of a plugin.
  5. It removes the safety net and can let broken code be packaged.

References

  • Apache Maven, Introduction to the Build Lifecycle. Supports the list of phases, the rule that a phase runs all earlier ones, clean as a separate lifecycle that removes files from the previous build, and the default goal bindings for JAR packaging.
  • All outputs shown come from Maven 3.9.11 on JDK 21; plugin versions in your output may differ. The explanation of -DskipTests comes from the build output shown, not from a page I fetched.

Practice

Apply this lesson with the module's lab, exercises and quiz.

Lab and exercises Quiz

Marking a lesson complete is optional and you can undo it any time. Your progress stays in this browser.

Continue to next lesson

Type to search levels, phases, modules and lessons.