Skip to content

Build Phase


A build is the process of transforming your raw source code into something that can actually run in production. Depending on the language/platform, this can include:

  • Compiling source code
  • Running unit tests
  • Resolving and downloading dependencies
  • Packaging into a deployable format (JAR, WAR, EXE, Docker image, etc.)
  • Updating database schemas
  • Compressing/minifying files

In the CI/CD pipeline, build automation sits right after a developer pushes code. The CI server picks it up, runs the build tool, and the output either passes down the pipeline or comes back for fixes — all without manual intervention.


  • Eliminates human error in repetitive steps
  • Every build is consistent and reproducible — works the same on your machine, your teammate’s machine, and the CI server
  • Faster feedback loop — developers know if something broke immediately
  • Enforces standards across the entire team
  • Foundation of CI/CD — without reliable builds, nothing downstream (testing, deployment) works

TermMeaning
Build ToolExecutes the build steps (compile, test, package)
Build Framework/SystemBroader ecosystem — dependency management + plugins + lifecycle

Maven and Gradle are both — they handle everything from fetching libraries off the internet to packaging your final artifact.


LanguageTool(s)
Java / JVMMaven, Gradle, Ant (legacy)
C / C++Make, CMake, Ninja, Bazel
Pythonpip + setuptools, Poetry, Tox, PyBuilder
JavaScript / Nodenpm scripts, Webpack, Vite, Rollup, Parcel, Turbo
GoBuilt-in go build, Makefile
RubyRake
.NET / C#MSBuild, dotnet CLI

Maven is a project management and build tool for Java. It works on the concept of a POM (Project Object Model) — a single pom.xml file describes everything about your project: what it is, what it depends on, how to build it.

  • Convention over configuration — Maven expects a standard folder structure. Follow it and you barely need to configure anything.
  • Central Repository — Dependencies (JARs) are downloaded automatically from Maven Central. No more manually hunting down .jar files.
  • Lifecycle-based — You don’t tell Maven how to do things step by step. You tell it what phase to run, and it figures out the steps.

Standard Project Structure (Maven enforces this)

Section titled “Standard Project Structure (Maven enforces this)”
my-app/
├── pom.xml
└── src/
├── main/
│ └── java/
│ └── com/example/App.java
└── test/
└── java/
└── com/example/AppTest.java
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>my-app</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

groupId → your org/package namespace

artifactId → your project name

version → current version

SNAPSHOT → means it’s still in development

These run sequentially — running package automatically runs everything before it.

validate → compile → test → package → verify → install → deploy
PhaseWhat happens
validateChecks pom.xml and project setup is correct
compileCompiles .java.class files
testRuns unit tests (JUnit etc.)
packageBundles compiled code into a JAR/WAR
verifyRuns integration checks on the package
installPuts the JAR into your local .m2 cache
deployPushes to a remote repo (Nexus, Artifactory etc.)

Two bonus lifecycles:

  • clean — deletes the /target folder (previous build outputs)
  • site — generates HTML project documentation
Terminal window
mvn validate # check project is ok
mvn compile # compile only
mvn test # compile + run tests
mvn package # compile + test + create JAR
mvn clean package # clean old build, then package fresh
mvn install # package + install to local .m2 repo
mvn clean install -DskipTests # skip tests during install
mvn dependency:tree # show full dependency tree

Key point: mvn clean package is what most CI pipelines run. It nukes the old build and creates a fresh JAR.


Gradle is a modern, flexible build automation tool. It’s the default build system for Android apps and increasingly popular for backend Java/Kotlin projects. Unlike Maven’s XML, Gradle uses a Groovy or Kotlin DSL script (build.gradle or build.gradle.kts).

FeatureMavenGradle
Config formatXML (pom.xml)Groovy/Kotlin DSL (build.gradle)
ApproachDeclarative, fixed lifecycleFlexible — define custom tasks
SpeedSlower (no incremental build)Faster — incremental builds, build cache
AndroidNot usedOfficial Android build tool
Learning curveEasier to startMore powerful but more to learn
my-app/
├── build.gradle
├── settings.gradle
└── src/
├── main/
│ └── java/
│ └── com/example/App.java
└── test/
└── java/
└── com/example/AppTest.java

Same src/main/java convention as Maven — they’re compatible.

plugins {
id 'java'
}
group = 'com.example'
version = '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
testImplementation 'junit:junit:4.13.2'
}

In Gradle, everything is a task. A build = a set of tasks. You can define custom tasks or use the built-in ones from plugins.

// Custom task example in build.gradle
task hello {
doLast {
println 'Hello from Gradle!'
}
}

Run it:

Terminal window
gradle hello
Terminal window
gradle tasks # list all available tasks
gradle compileJava # compile only
gradle test # run tests
gradle build # compile + test + package (creates JAR in /build/libs)
gradle clean # delete /build folder
gradle clean build # clean then full build
gradle build -x test # build but skip tests
gradle dependencies # show dependency tree

Key point: gradle clean build is the CI/CD standard command for Gradle projects — same idea as Maven’s clean package.

In real projects, you’ll almost always see ./gradlew instead of gradle. This is the Gradle Wrapper — a script bundled with the project that downloads and uses the exact Gradle version the project was built with. No “works on my machine” issues.

Terminal window
./gradlew build # Linux/Mac
gradlew.bat build # Windows

Practicing Maven & Gradle in VSCode (Java 20+ only, no complex setup)

Section titled “Practicing Maven & Gradle in VSCode (Java 20+ only, no complex setup)”

You don’t need to install Maven or Gradle globally. Here’s the clean way to do it in VSCode.

Install “Extension Pack for Java” by Microsoft from the VSCode marketplace. This single pack includes:

  • Language Support for Java (Red Hat)
  • Maven for Java
  • Gradle for Java
  • Java Test Runner
  • Debugger for Java

Step 2 — Maven in VSCode (no Maven install needed)

Section titled “Step 2 — Maven in VSCode (no Maven install needed)”

The Maven extension downloads Maven automatically (or uses an embedded version). You can:

Create a Maven project:

  1. Ctrl+Shift+PMaven: Create Maven Project
  2. Select archetype → maven-archetype-quickstart
  3. Fill in groupId, artifactId → project is scaffolded with correct structure

Or from terminal (VSCode’s integrated terminal):

Terminal window
mvn archetype:generate \
-DgroupId=com.example \
-DartifactId=my-app \
-DarchetypeArtifactId=maven-archetype-quickstart \
-DinteractiveMode=false

Run build tasks via the Maven sidebar (appears after extension install) — you’ll see your project’s lifecycle phases as clickable buttons. No terminal needed.

Step 3 — Gradle in VSCode (using Gradle Wrapper — no Gradle install needed)

Section titled “Step 3 — Gradle in VSCode (using Gradle Wrapper — no Gradle install needed)”

The trick: use the Gradle Wrapper bundled with your project.

Create a Gradle project using VSCode:

  1. Ctrl+Shift+PGradle: Create a Gradle Java Project
  2. Choose project folder, name, DSL (Groovy or Kotlin)

This scaffolds the project including the Gradle wrapper (gradlew). From the terminal:

Terminal window
./gradlew build # wrapper auto-downloads correct Gradle version
./gradlew tasks # see available tasks

The Gradle for Java extension also gives you a sidebar panel showing all tasks as a tree — click to run without typing commands.

With Java 20+ installed and the Extension Pack for Java in VSCode, you get full Maven and Gradle support with zero manual installation of either tool. Maven is embedded in the extension. Gradle uses its wrapper (gradlew) which auto-downloads itself.


The JS ecosystem has several layers of “build tooling”:

Package managers (also run scripts):

Terminal window
npm run build # runs whatever "build" script is in package.json
yarn build

Bundlers (bundle many JS files into one optimized output):

ToolUse case
WebpackMature, highly configurable, used in React (CRA)
ViteFast modern bundler, default for Vue, React (latest), Svelte
RollupBest for building libraries
ParcelZero-config bundler
esbuildExtremely fast, used inside Vite
Turbo (Turborepo)Monorepo build orchestration

In a CI pipeline for a frontend app, npm run build triggers the bundler, which minifies/compresses assets and outputs a /dist folder that gets deployed.

Python doesn’t compile in the traditional sense, but build tools handle packaging, dependency management, and distribution:

ToolPurpose
pip + setuptoolsBasic packaging, setup.py
PoetryModern dependency management + packaging (pyproject.toml)
ToxRuns tests in multiple Python environments
PyBuilderFull build lifecycle tool similar to Maven
HatchModern Python project management
Terminal window
poetry install # install dependencies
poetry build # build a distributable package (wheel/sdist)
tox # run test suite across environments
ToolNotes
MakeThe classic — uses Makefile, been around since 1976
CMakeGenerates build files for Make/Ninja/MSVC etc. Industry standard
NinjaUltra-fast build executor, used with CMake
BazelGoogle’s build system, handles massive monorepos
MesonModern alternative to CMake, faster
Terminal window
# CMake workflow
cmake -B build # configure, generate build files into /build
cmake --build build # actually compile

CMake doesn’t build directly — it generates the instructions for another tool (like Make or Ninja) which does the actual build. Two-step process.

Go has build tooling built into the language itself — no separate tool needed:

Terminal window
go build ./... # compile everything
go test ./... # run all tests
go install # compile + install binary to $GOPATH/bin

For more complex orchestration, Go projects often use a Makefile:

build:
go build -o bin/app ./cmd/app
test:
go test ./...
Terminal window
dotnet build # compile
dotnet test # run tests
dotnet publish # create deployable output

The dotnet CLI handles everything. MSBuild runs under the hood.


Let’s build a small but real project: a Student Grade Calculator with two classes and a test.

student-grades/
├── src/
│ ├── main/java/com/school/
│ │ ├── Student.java
│ │ └── GradeCalculator.java
│ └── test/java/com/school/
│ └── GradeCalculatorTest.java

Student.java

package com.school;
public class Student {
private String name;
private double[] marks;
public Student(String name, double[] marks) {
this.name = name;
this.marks = marks;
}
public String getName() { return name; }
public double[] getMarks() { return marks; }
}

GradeCalculator.java

package com.school;
public class GradeCalculator {
public double calculateAverage(Student student) {
double sum = 0;
for (double mark : student.getMarks()) {
sum += mark;
}
return sum / student.getMarks().length;
}
public String getGrade(double average) {
if (average >= 90) return "A";
else if (average >= 75) return "B";
else if (average >= 60) return "C";
else if (average >= 50) return "D";
else return "F";
}
}

GradeCalculatorTest.java

package com.school;
import org.junit.Test;
import static org.junit.Assert.*;
public class GradeCalculatorTest {
@Test
public void testAverageCalculation() {
Student s = new Student("Alice", new double[]{85, 90, 78, 92});
GradeCalculator calc = new GradeCalculator();
assertEquals(86.25, calc.calculateAverage(s), 0.01);
}
@Test
public void testGradeB() {
GradeCalculator calc = new GradeCalculator();
assertEquals("B", calc.getGrade(80));
}
@Test
public void testGradeF() {
GradeCalculator calc = new GradeCalculator();
assertEquals("F", calc.getGrade(40));
}
}

pom.xml

<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.school</groupId>
<artifactId>student-grades</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>20</maven.compiler.source>
<maven.compiler.target>20</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

Run the build:

Terminal window
mvn clean package

What happens step by step:

[INFO] --- maven-clean-plugin: Deleting /target ---
[INFO] --- maven-compiler-plugin: Compiling 2 source files to /target/classes ---
[INFO] --- maven-compiler-plugin: Compiling 1 test source ---
[INFO] --- maven-surefire-plugin: Running GradeCalculatorTest ---
[INFO] Tests run: 3, Failures: 0, Errors: 0
[INFO] --- maven-jar-plugin: Building jar /target/student-grades-1.0-SNAPSHOT.jar ---
[INFO] BUILD SUCCESS

Output: target/student-grades-1.0-SNAPSHOT.jar

Run it (if you add a main method):

Terminal window
java -cp target/student-grades-1.0-SNAPSHOT.jar com.school.Main

Same source files. Different config.

settings.gradle

rootProject.name = 'student-grades'

build.gradle

plugins {
id 'java'
}
group = 'com.school'
version = '1.0-SNAPSHOT'
java {
sourceCompatibility = JavaVersion.VERSION_20
targetCompatibility = JavaVersion.VERSION_20
}
repositories {
mavenCentral()
}
dependencies {
testImplementation 'junit:junit:4.13.2'
}
// Optional: custom task to print project info
task projectInfo {
doLast {
println "Project: ${project.name} v${project.version}"
println "Source sets: ${sourceSets.main.java.srcDirs}"
}
}

Run the build:

Terminal window
./gradlew clean build

What happens:

> Task :clean
> Task :compileJava (compiles Student.java, GradeCalculator.java)
> Task :processResources (copies any resources)
> Task :classes
> Task :compileTestJava (compiles GradeCalculatorTest.java)
> Task :processTestResources
> Task :testClasses
> Task :test (runs 3 tests — all pass)
> Task :jar (packages into JAR)
> Task :build
BUILD SUCCESSFUL in 3s
7 actionable tasks: 7 executed

Output: build/libs/student-grades-1.0-SNAPSHOT.jar

Run your custom task:

[src/main/java]
./gradlew projectInfo
# Project: student-grades v1.0-SNAPSHOT

SituationGo with
Enterprise Java project, existing teamMaven (everybody knows it)
Android appGradle (mandatory)
Need faster incremental builds in large projectsGradle
Microservices, Spring BootEither — Spring Initializr supports both
Need custom build logic / scriptingGradle
Starting fresh, team prefers XMLMaven
Starting fresh, team prefers code-like configGradle

Bottom line for CI/CD: Whether it’s mvn clean package or ./gradlew clean build, the CI server runs one command, gets one artifact, and passes it down the pipeline. The build tool is the engine — the pipeline is the assembly line.