Deepen PracticeOrdered learning track

CI/CD Pipeline Design for Maven

Learn Maven In Action - Part 032

Production-grade CI/CD pipeline design for Maven: validation stages, cache strategy, multi-module builds, artifact handoff, secure publishing, matrix builds, release gates, failure isolation, and pipeline architecture.

21 min read4073 words
PrevNext
Lesson 3240 lesson track2333 Deepen Practice
#maven#java#ci-cd#build-engineering+2 more

Part 032 — CI/CD Pipeline Design for Maven

A Maven CI/CD pipeline is not just this:

mvn clean install

That command may be acceptable on a laptop.

It is not a pipeline design.

A production-grade Maven pipeline answers deeper questions:

  • Which code changes are allowed to enter the main branch?
  • Which Maven phases are run at which gate?
  • Which artifacts are produced?
  • Which artifacts are published?
  • Which dependencies are allowed?
  • Which JDK and Maven versions are used?
  • How is the local Maven repository cached?
  • How are flaky tests isolated?
  • How are integration test environments provisioned and cleaned up?
  • How is a release artifact traced back to source?
  • How do we prevent publishing broken or non-reproducible artifacts?

This part builds a mental model for designing Maven CI/CD pipelines that hold up under large teams, multi-module codebases, internal libraries, regulated change control, and release audit requirements.


1. Pipeline as a State Machine

Think of CI/CD as a state machine for code and artifacts.

Each state has invariants.

StateInvariant
CommitSource is identifiable by commit SHA.
ValidateBuild metadata is structurally valid.
TestFast correctness checks pass.
PackageArtifact can be assembled.
VerifyFull build verification passes.
Publish snapshotMutable integration artifact is available.
Stage releaseCandidate artifact exists but is not final.
ScanCandidate passes security/license/compliance gates.
PromoteExact candidate becomes release artifact.

A pipeline is weak when states are collapsed without understanding.

Example weak pipeline:

mvn clean deploy -DskipTests

This jumps directly from source to shared artifact while bypassing verification.

That is not automation maturity. That is automated risk.


2. Maven Phase Mapping to CI Stages

Maven already has lifecycle phases. A good pipeline maps CI stages to Maven phases intentionally.

CI StageMaven commandPurpose
Model validationmvn validatePOM structure, enforcer, version policy, basic config.
Compilemvn compileLanguage/API compatibility.
Unit testmvn testFast isolated tests via Surefire.
Packagemvn packageArtifact assembly.
Full verificationmvn verifyIntegration tests, quality checks, packaging checks.
Local publish simulationmvn installArtifact resolvable locally.
Remote publishmvn deployArtifact shared to remote repository.

In most CI jobs, mvn verify is the default quality gate.

Why verify?

Because verify runs later than package and is the correct lifecycle point for checks that validate the packaged artifact.

Avoid making mvn install your default CI validation command unless another module/project needs local repository installation in the same job.

For ordinary validation:

mvn --batch-mode --no-transfer-progress clean verify

For publication:

mvn --batch-mode --no-transfer-progress clean deploy

3. The Minimal Healthy Maven CI Command

Baseline command:

mvn --batch-mode --no-transfer-progress clean verify

Meaning:

OptionWhy it matters
--batch-modeNon-interactive CI execution.
--no-transfer-progressCleaner logs, less noise.
cleanReduces stale target/ contamination.
verifyRuns lifecycle through verification gates.

For debugging, you may add:

-e

For deep Maven internals debugging:

-X

Do not run -X permanently in CI.

Debug logs are noisy and can expose environment details.


4. Do Not Default to mvn clean install

Many teams use:

mvn clean install

It works, but it often hides weak pipeline thinking.

install writes artifacts to the local Maven repository of the CI runner.

That can be useful when:

  • later steps in the same job consume the artifact;
  • a separate integration test project needs the artifact;
  • you are testing publication metadata locally.

But it is unnecessary if the job only validates the project.

Prefer:

mvn clean verify

Use install only when you need local repository side effects.

This matters especially with caching.

A polluted CI local repository can hide missing dependencies, broken publication, or undeclared module relationships.


5. The Pipeline Layers

A strong Maven pipeline has layers.

Each layer answers a different question.

LayerQuestion
PR validationIs this change safe to merge?
Main verificationIs main still releasable?
Snapshot publicationCan other teams integrate with latest main?
Release stagingIs this exact artifact a candidate?
Security/license gatesIs it allowed to ship?
Consumer smoke testCan real consumers resolve and start with it?
PromotionShould this artifact become a release?
DeploymentShould this artifact go to an environment?

Do not force every pull request to run every possible gate.

That creates slow feedback and developer bypass behavior.

Instead, design gates by risk and cost.


6. PR Validation Pipeline

Goal:

Catch most problems before merge, quickly.

Typical PR command:

mvn --batch-mode --no-transfer-progress clean verify

For huge builds, split into stages:

1. validate + enforcer
2. compile
3. unit tests
4. package + integration tests for affected modules

PR validation should include:

  • POM validity;
  • Enforcer rules;
  • compile;
  • unit tests;
  • static quality checks;
  • selected integration tests;
  • dependency convergence checks;
  • generated source checks;
  • no accidental version changes;
  • no snapshot dependency in protected modules unless allowed.

PR validation may skip:

  • artifact deploy;
  • full system test;
  • long performance test;
  • manual approval;
  • production deployment.

But skipping must be explicit.

Do not hide skipped checks behind random -DskipTests.

Use named profiles/properties with clear semantics.

Bad:

mvn clean install -DskipTests

Better:

mvn clean verify -Pfast-pr

Then document exactly what fast-pr excludes.


7. Main Branch Pipeline

Goal:

Keep main branch releasable.

Main branch should usually run stricter gates than PR.

Example:

mvn --batch-mode --no-transfer-progress clean verify

Additional main branch steps:

  • full integration tests;
  • package inspection;
  • SBOM generation;
  • vulnerability scan;
  • publish snapshot if policy allows;
  • update build status dashboard;
  • produce test reports;
  • archive build logs/artifacts.

If main branch publishes snapshots:

mvn --batch-mode --no-transfer-progress clean deploy

Only if version is -SNAPSHOT.

Never let main publish releases automatically unless the organization has a deliberate continuous-release model.


8. Release Pipeline

Goal:

Produce or promote an immutable artifact with provenance.

A release pipeline should run from:

  • a Git tag;
  • a release branch;
  • a manually approved workflow;
  • a versioned release request;
  • a generated release commit.

Release pipeline stages:

1. checkout exact source
2. set up exact JDK/Maven/toolchain
3. verify no SNAPSHOT version/dependencies
4. run full tests
5. package artifacts
6. sign artifacts if required
7. deploy to staging repository
8. generate/attach SBOM
9. scan candidate
10. run clean-room consumer test
11. promote staging repository
12. publish release metadata

Release command often starts with:

mvn --batch-mode --no-transfer-progress clean deploy

But the command alone is not the release pipeline.

The gates around it make it a release pipeline.


9. Artifact Handoff Principle

The artifact that passed gates should be the artifact that gets released.

Weak handoff:

Job A: mvn verify
Job B: mvn deploy   # rebuilds from source

This assumes rebuild is identical.

That may be true if reproducible builds are strong. But many Maven builds are not perfectly deterministic because of timestamps, generated sources, environment variables, plugin behavior, or dependency drift.

Stronger handoff:

Job A: build + test + deploy to staging
Job B: promote same staged artifacts

Alternative:

Job A: build + test + archive artifacts
Job B: publish exact archived artifacts

For Maven libraries, repository staging is often the cleanest handoff.

For application deployments, container/image pipelines may take over after Maven package.


10. Cache Strategy

Maven downloads dependencies and plugins into the local repository.

Default local repository:

~/.m2/repository

Caching this directory can speed up CI.

But caching has risks:

  • stale snapshots;
  • corrupted artifacts;
  • hidden undeclared dependencies;
  • cross-branch contamination;
  • cache poisoning;
  • inconsistent plugin versions;
  • secrets accidentally stored outside intended locations.

Good cache key includes:

  • operating system;
  • Java version;
  • Maven version if controlled;
  • hash of relevant POM files;
  • possibly settings/repository policy version.

Example conceptual key:

m2-${os}-jdk${javaVersion}-${hashOfPomFiles}

Use dependency cache to reduce network cost, not to make builds pass.

A build should pass from an empty local repository.

Run clean-room builds periodically:

mvn -Dmaven.repo.local=/tmp/m2-clean clean verify

11. GitHub Actions Example

A minimal Maven validation workflow:

name: Maven CI

on:
  pull_request:
  push:
    branches: [ main ]

jobs:
  verify:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up JDK
        uses: actions/setup-java@v4
        with:
          distribution: temurin
          java-version: '21'
          cache: maven

      - name: Verify
        run: mvn --batch-mode --no-transfer-progress clean verify

GitHub Actions supports Maven dependency caching through Java setup/cache mechanisms, but the exact action versions should be pinned and maintained by your platform team.

For enterprise CI, also consider pinning actions by SHA according to security policy.


12. GitHub Actions Publish Example

Snapshot deploy from main:

name: Maven Snapshot Publish

on:
  push:
    branches: [ main ]

jobs:
  deploy-snapshot:
    runs-on: ubuntu-latest
    permissions:
      contents: read

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up JDK
        uses: actions/setup-java@v4
        with:
          distribution: temurin
          java-version: '21'
          cache: maven

      - name: Generate Maven settings
        run: |
          mkdir -p .ci
          cat > .ci/settings.xml <<'SETTINGS'
          <settings xmlns="http://maven.apache.org/SETTINGS/1.2.0">
            <servers>
              <server>
                <id>acme-snapshots</id>
                <username>${env.MAVEN_REPO_USERNAME}</username>
                <password>${env.MAVEN_REPO_TOKEN}</password>
              </server>
            </servers>
          </settings>
          SETTINGS

      - name: Deploy snapshot
        env:
          MAVEN_REPO_USERNAME: ${{ secrets.MAVEN_REPO_USERNAME }}
          MAVEN_REPO_TOKEN: ${{ secrets.MAVEN_REPO_TOKEN }}
        run: |
          mvn --batch-mode --no-transfer-progress \
            --settings .ci/settings.xml \
            clean deploy

Hardening additions:

  • branch protection;
  • environment protection for releases;
  • secret access only on trusted branches;
  • no secrets on untrusted pull requests;
  • clean-room dependency resolution job;
  • dependency review/scanning;
  • action pinning;
  • artifact provenance metadata.

13. Jenkins Pipeline Example

Declarative pipeline skeleton:

pipeline {
  agent any

  tools {
    jdk 'jdk-21'
    maven 'maven-3.9.x'
  }

  options {
    timestamps()
    ansiColor('xterm')
    disableConcurrentBuilds()
  }

  stages {
    stage('Checkout') {
      steps {
        checkout scm
      }
    }

    stage('Verify') {
      steps {
        sh 'mvn --batch-mode --no-transfer-progress clean verify'
      }
      post {
        always {
          junit '**/target/surefire-reports/*.xml'
          junit '**/target/failsafe-reports/*.xml'
        }
      }
    }

    stage('Deploy Snapshot') {
      when {
        branch 'main'
      }
      steps {
        withCredentials([usernamePassword(
          credentialsId: 'maven-snapshots',
          usernameVariable: 'MAVEN_REPO_USERNAME',
          passwordVariable: 'MAVEN_REPO_TOKEN'
        )]) {
          sh 'mvn --batch-mode --no-transfer-progress --settings .ci/settings.xml clean deploy'
        }
      }
    }
  }
}

For release pipelines, add approval and staging promotion stages.


14. Multi-Module CI Strategy

Multi-module builds introduce tradeoffs.

Full reactor build:

mvn clean verify

Pros:

  • highest confidence;
  • catches cross-module issues;
  • simple mental model.

Cons:

  • can be slow;
  • poor feedback for small changes;
  • expensive in large monorepos.

Partial affected-module build:

mvn clean verify -pl payment-api -am

Pros:

  • faster;
  • useful for PR feedback;
  • builds upstream dependencies automatically.

Cons:

  • may miss downstream breakage;
  • requires changed-module detection;
  • can hide integration issues.

Downstream impact build:

mvn clean verify -pl payment-api -amd

Pros:

  • tests modules affected by a change;
  • useful for API modules.

Cons:

  • can become almost full build;
  • dependency graph must be correct.

Recommended pattern:

TriggerBuild strategy
Small PRAffected modules + upstream dependencies.
API contract changeAffected module + downstream modules.
Main branchFull reactor.
ReleaseFull reactor + staging gates.
NightlyFull reactor + slow tests + clean cache.

15. Parallel Maven Builds

Maven supports parallel builds with -T.

Example:

mvn -T 1C clean verify

1C means one thread per CPU core.

Parallel builds can reduce wall-clock time.

But they expose hidden problems:

  • non-thread-safe plugins;
  • tests sharing ports/files/global state;
  • integration tests using fixed resources;
  • generated source races;
  • modules depending on files without declared Maven dependency;
  • flaky ordering assumptions.

Use parallel builds after the reactor graph is healthy.

Do not use parallelism to hide slow architecture.

CI strategy:

PR fast build:     mvn -T 1C clean verify
Nightly strict:    mvn clean verify
Release build:     mvn clean deploy   # parallel only if proven safe

For release, prefer reliability over speed.


16. Test Stage Design

Maven test execution should align with Surefire/Failsafe.

Test typePluginPhaseCI placement
Unit testSurefiretestPR and main.
Integration testFailsafeintegration-test + verifyMain/release, selected PR.
Contract testSurefire/Failsafe/customDependsPR for API changes, main.
Smoke testExternal step or FailsafeAfter package/deployRelease/staging.
Performance testSeparate tooling/profileNot default lifecycleNightly/release candidate.

Do not put slow environment-heavy tests in Surefire by accident.

Use naming conventions:

*Test.java      -> unit tests
*IT.java        -> integration tests

Run full verification through:

mvn verify

Not:

mvn integration-test

Because verify gives Failsafe a chance to evaluate results and lets post-integration cleanup phases run as designed.


17. Profiles in CI

Profiles should make pipeline intent explicit, not hide environment chaos.

Good profile names:

fast-pr
full-ci
release
integration-tests
skip-docker-it

Bad profile names:

dev
qa
prod
local
jenkins

Example:

mvn clean verify -Pfull-ci

Profile rules:

  • default build should be meaningful;
  • CI profiles should be documented;
  • release profile should be reproducible;
  • avoid profiles that change dependency versions by environment;
  • avoid profiles that create different artifacts for dev/qa/prod;
  • secrets should not be injected by resource filtering.

A CI profile may enable stricter checks:

<profile>
  <id>full-ci</id>
  <build>
    <plugins>
      <!-- quality, coverage, integration test plugins -->
    </plugins>
  </build>
</profile>

A release profile may attach sources, javadocs, signatures, SBOM.


18. Build Matrix Strategy

Matrix builds test compatibility across JDKs, operating systems, or Maven versions.

Example dimensions:

JDK: 17, 21
OS: ubuntu-latest, windows-latest
Maven: controlled wrapper version or installed Maven

Use matrix builds when:

  • publishing libraries;
  • supporting multiple Java baselines;
  • using native components;
  • tests depend on filesystem behavior;
  • annotation processors behave differently;
  • toolchain compatibility matters.

Do not blindly run huge matrices on every PR.

Suggested:

TriggerMatrix
PRPrimary supported JDK/OS.
MainPrimary + secondary JDK.
NightlyFull JDK/OS matrix.
ReleaseRequired support matrix.

For libraries, the release matrix is part of the consumer contract.

For services, matrix may be smaller because runtime platform is controlled.


19. Maven Wrapper in CI

Many teams use Maven Wrapper:

mvnw
mvnw.cmd
.mvn/wrapper/maven-wrapper.properties

Benefits:

  • project pins Maven distribution;
  • CI and developer use same Maven version;
  • easier onboarding;
  • less global tool drift.

CI command:

./mvnw --batch-mode --no-transfer-progress clean verify

Governance:

  • wrapper JAR/script provenance should be controlled;
  • wrapper version should be reviewed;
  • update wrapper intentionally;
  • do not mix random system Maven and wrapper Maven in the same pipeline without reason.

Maven version is part of the build environment.

Treat it like JDK version.


20. .mvn/maven.config and .mvn/jvm.config

Project-level Maven config can standardize command options.

Example .mvn/maven.config:

--batch-mode
--no-transfer-progress

Example .mvn/jvm.config:

-Xmx2g
-Dfile.encoding=UTF-8

Benefits:

  • reduces command duplication;
  • aligns local and CI behavior;
  • makes default build policy visible.

Risks:

  • hidden flags surprise maintainers;
  • local-only assumptions leak into CI;
  • memory flags may not suit all runners;
  • profile activation may become implicit.

Guideline:

Use .mvn config for stable build behavior.

Do not hide release decisions in .mvn/maven.config.


21. Environment Isolation

CI must control environment inputs.

Important inputs:

  • JDK version;
  • Maven version;
  • OS image;
  • timezone;
  • locale;
  • file encoding;
  • local repository path;
  • network access;
  • settings.xml;
  • environment variables;
  • Docker daemon availability;
  • external service dependencies.

Set explicitly where relevant:

export TZ=UTC
export MAVEN_OPTS="-Dfile.encoding=UTF-8"

POM properties:

<properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>

For reproducible artifacts, also control:

<project.build.outputTimestamp>${git.commit.time}</project.build.outputTimestamp>

Or another deterministic timestamp source.


22. Secure Settings and Secret Handling

CI secrets must be available only to jobs that need them.

PRs from untrusted forks should not receive deploy credentials.

Pattern:

JobNeeds Maven repo credentials?
PR verifyNo, except read-only repo if private dependencies require it.
Main verifyMaybe read-only.
Snapshot publishYes, snapshot deploy token.
Release stagingYes, staging deploy token.
PromotionYes, promotion token/API credentials.

Keep write credentials out of validation jobs.

Use separate credentials for:

  • dependency read;
  • snapshot deploy;
  • release staging deploy;
  • promotion;
  • public publishing.

If a job only needs read access, do not give deploy access.


23. CI Repository Access Pattern

Ideal pattern:

CI should normally resolve through one internal group/virtual URL.

Benefits:

  • audit dependency downloads;
  • cache external dependencies;
  • centralize allow/block rules;
  • survive external outages better;
  • prevent direct access to random repositories;
  • support air-gapped or restricted environments.

Configure this through settings.xml mirrors, not repeated POM repositories.


24. Artifact and Report Archiving

CI should preserve useful evidence.

Archive:

  • test reports;
  • failsafe reports;
  • coverage reports;
  • generated SBOM;
  • packaged artifact for non-published builds;
  • effective POM for release builds;
  • dependency tree for release builds;
  • build scan/log summary if available;
  • checksums for release artifacts.

Example commands:

mvn dependency:tree -DoutputFile=target/dependency-tree.txt
mvn help:effective-pom -Doutput=target/effective-pom.xml

For release builds, these files are audit artifacts.

Do not rely only on console logs.


25. Dependency Tree Gate

Dependency problems often enter silently.

Useful CI checks:

mvn dependency:tree
mvn dependency:analyze
mvn enforcer:enforce

Release build should capture dependency tree:

mvn --batch-mode dependency:tree \
  -DoutputFile=target/dependency-tree.txt

For multi-module projects, capture per-module dependency trees if needed.

Policy examples:

  • no duplicate logging bindings;
  • no banned legacy JSON library;
  • no vulnerable dependency version;
  • no SNAPSHOT dependency in release;
  • no test framework in compile scope;
  • no internal implementation module dependency from public API module.

Make dependency policy executable.

Do not keep it only in wiki pages.


26. SBOM in CI

A modern Maven pipeline should be able to produce a Software Bill of Materials.

Typical placement:

verify/package -> generate SBOM -> scan -> archive/attach

SBOM usage:

  • vulnerability analysis;
  • license review;
  • incident response;
  • customer/regulator requests;
  • artifact provenance;
  • dependency inventory.

The important design question is not merely “which plugin?”

It is:

Which artifact does this SBOM describe?

For libraries, it may describe the published Maven artifact.

For applications, it may need to describe the deployable image, not only the Maven dependency graph.

Maven dependency SBOM is necessary but may not be sufficient for containerized services.


27. Release Provenance

Every release artifact should answer:

What source produced this artifact?
What command produced it?
What JDK/Maven produced it?
What dependencies were resolved?
What checks passed?
Who/what approved it?
Where was it published?
What checksum identifies it?

Practical metadata to capture:

  • Git commit SHA;
  • Git tag;
  • branch;
  • CI run ID;
  • JDK version;
  • Maven version;
  • OS image;
  • effective POM;
  • dependency tree;
  • artifact checksum;
  • repository URL;
  • staging repository ID;
  • promotion timestamp;
  • approver identity if manual.

This is not bureaucracy.

This is how you debug production dependency incidents quickly.


28. Failure Isolation

When a pipeline fails, isolate the failure layer.

SymptomLikely layer
POM parse errorModel/config.
Dependency not foundRepository/resolution.
Compilation failsSource/API/JDK.
Unit tests failCode behavior.
Integration tests hangEnvironment/resource cleanup.
Package missing filesPlugin/layout/resource config.
Enforcer failsGovernance/dependency policy.
Deploy returns 401Credentials/server ID.
Deploy returns 403Permission/repository policy.
Redeploy conflictImmutable release/version reuse.
Works locally but not CIEnvironment/cache/settings difference.

Debug workflow:

1. Identify failed stage.
2. Identify Maven phase/goal that failed.
3. Reproduce with same command locally or in CI shell if allowed.
4. Check effective POM/settings.
5. Check dependency tree/resolution.
6. Check environment variables/toolchain.
7. Check repository manager logs if deploy/resolve failure.

Do not start by changing plugin versions randomly.


29. Flaky Tests and Pipeline Trust

Flaky tests destroy CI trust.

Bad response:

-DskipTests

Better response:

  • classify flaky tests;
  • quarantine only with ticket/owner/expiry;
  • collect failure statistics;
  • separate environment flakes from logic flakes;
  • fix shared ports/time/files/randomness;
  • make integration test cleanup reliable;
  • use deterministic clocks where possible;
  • fail release pipeline on quarantined critical tests.

Maven-level tools:

  • Surefire/Failsafe rerun configuration may reduce transient noise;
  • but reruns should be visible;
  • reruns are not a substitute for fixing nondeterminism.

Pipeline invariant:

A green release pipeline must mean something.

If everyone knows green sometimes means lucky, the pipeline has lost authority.


30. Branch and Version Policy

Pipeline behavior should follow branch/version rules.

Example policy:

SourceVersionAllowed action
feature branch*-SNAPSHOTverify only, maybe branch snapshot.
main*-SNAPSHOTverify + publish main snapshot.
release branchrelease candidate versionstage candidate.
Git tag v1.4.01.4.0release staging/promotion.
hotfix branch1.4.1-SNAPSHOT -> 1.4.1hotfix release flow.

Automate checks:

  • tag matches project version;
  • release version is non-SNAPSHOT;
  • main branch does not accidentally carry release version forever;
  • no duplicate release tag;
  • no redeploy of existing immutable version.

Example simple shell guard:

PROJECT_VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)
case "$PROJECT_VERSION" in
  *-SNAPSHOT) echo "Snapshot version: $PROJECT_VERSION" ;;
  *) echo "Release version: $PROJECT_VERSION" ;;
esac

31. Container Build Boundary

For Java services, Maven often feeds a container pipeline.

Two patterns:

Pattern A — Maven builds JAR, container tool builds image

mvn package -> target/app.jar -> docker build -> image registry

Pros:

  • clear separation;
  • container tool owns image;
  • Maven remains Java build tool.

Cons:

  • artifact handoff must be controlled;
  • Dockerfile must match artifact name/version.

Pattern B — Maven plugin builds image

mvn package jib:build

Pros:

  • integrated with Maven lifecycle;
  • can use Maven metadata;
  • useful for Java-only services.

Cons:

  • build system now owns container publication too;
  • plugin configuration becomes release-critical;
  • image provenance must be captured.

Either is fine if the boundary is explicit.

Do not publish Maven artifact and container image from disconnected rebuilds unless reproducibility is proven.


32. Monorepo vs Polyrepo Maven Pipelines

Polyrepo

Each repository has its own Maven build and release.

Pros:

  • simple ownership;
  • smaller pipelines;
  • independent releases.

Cons:

  • cross-repository dependency coordination;
  • snapshot dependency temptation;
  • platform BOM discipline required.

Monorepo

Many modules/services in one repository.

Pros:

  • atomic refactoring;
  • shared reactor visibility;
  • easier API impact testing.

Cons:

  • large build time;
  • changed-module detection complexity;
  • ownership boundaries harder;
  • partial release complexity.

Maven reactor works naturally for multi-module repositories, but very large monorepos may need additional build orchestration.

The Maven invariant remains:

If module B depends on module A, express that dependency in Maven metadata, not only in pipeline ordering.


33. CI Performance Principles

Performance tuning comes later in the series, but pipeline design should already avoid obvious waste.

Principles:

  • use dependency cache carefully;
  • avoid clean in every internal stage if same workspace is reused intentionally;
  • split fast and slow checks;
  • avoid repeated full rebuilds across jobs unless needed;
  • archive artifacts for handoff;
  • use -pl/-am for PR feedback;
  • run full build on main/release;
  • avoid unnecessary install;
  • avoid downloading from the internet directly;
  • pin plugin versions to reduce metadata churn.

Slow pipelines encourage bypasses.

But fast pipelines that miss real failures are worse.

Optimize after correctness boundaries are clear.


34. Pipeline Templates as Platform Product

In mature organizations, Maven pipelines are not copy-pasted YAML in every repo.

They are platform products.

A platform team may provide:

  • reusable GitHub Actions workflow;
  • Jenkins shared library;
  • internal Maven parent POM;
  • standard settings.xml generator;
  • repository manager conventions;
  • standard release workflow;
  • dependency policy rules;
  • quality gate profiles;
  • troubleshooting docs;
  • onboarding sample repository.

This prevents drift.

But platform abstractions must remain debuggable.

Engineers should still be able to answer:

What Maven command ran?
Which settings.xml was used?
Which profile was active?
Which repository URL was used?
Which artifact coordinates were published?

If the platform hides those answers, it is too magical.


35. Example Enterprise Pipeline Blueprint

Commands:

PR:

mvn --batch-mode --no-transfer-progress clean verify -Pfast-pr

Main:

mvn --batch-mode --no-transfer-progress clean verify -Pfull-ci

Snapshot publish:

mvn --batch-mode --no-transfer-progress --settings .ci/settings.xml clean deploy

Release staging:

mvn --batch-mode --no-transfer-progress --settings .ci/settings.xml clean deploy -Prelease

Clean-room consumer:

mvn -Dmaven.repo.local=/tmp/m2-clean \
  --settings .ci/settings.xml \
  dependency:get \
  -Dartifact=com.acme.payment:payment-api:1.4.0

36. Pipeline Anti-Patterns

Anti-pattern: -DskipTests in release pipeline

This creates unaudited risk.

If tests are too slow or flaky, fix test architecture.

Do not redefine release to mean “compiled”.

Anti-pattern: PR and release use different build logic

If PR build does not resemble release build, defects escape.

Some differences are acceptable, but they must be intentional and documented.

Anti-pattern: pipeline deploys from feature branches by default

This pollutes repositories and creates dependency confusion.

Branch snapshots can exist, but isolate them and expire them.

Anti-pattern: CI uses random Maven/JDK from runner image

Runner images change.

Pin JDK and Maven or use Maven Wrapper/toolchains.

Anti-pattern: cache required for success

A build that only works with warm cache is broken.

Periodically test with empty local repo.

Anti-pattern: release rebuild after validation

If the released artifact is rebuilt after gates, the gates did not validate the released bytes.

Use staging/promotion or reproducible build proof.


37. Operational Runbook: CI Build Fails Only in CI

Follow this sequence:

  1. Capture exact Maven command.
  2. Capture JDK version:
java -version
  1. Capture Maven version:
mvn -version
  1. Capture active profiles:
mvn help:active-profiles
  1. Capture effective POM:
mvn help:effective-pom -Doutput=target/effective-pom.xml
  1. Capture effective settings:
mvn help:effective-settings -Doutput=target/effective-settings.xml
  1. Run with clean local repository:
mvn -Dmaven.repo.local=/tmp/m2-clean clean verify
  1. Compare with local environment.

Common differences:

  • local has artifact installed in ~/.m2;
  • CI cannot access private repository;
  • active profile differs;
  • environment variable missing;
  • OS path separator/case sensitivity;
  • JDK version differs;
  • locale/timezone differs;
  • integration test service unavailable;
  • generated file checked in locally but missing in clean checkout.

38. Operational Runbook: Deploy Fails in CI

  1. Confirm version:
mvn help:evaluate -Dexpression=project.version -q -DforceStdout
  1. Confirm target from effective POM:
mvn help:effective-pom -Doutput=target/effective-pom.xml
  1. Confirm server IDs from settings:
mvn help:effective-settings -Doutput=target/effective-settings.xml
  1. Confirm CI secrets are present without printing them:
test -n "$MAVEN_REPO_USERNAME"
test -n "$MAVEN_REPO_TOKEN"
  1. Check status code:
CodeMeaning
401Missing/wrong credentials.
403Authenticated but not allowed.
404Wrong URL or repository path.
409Version conflict/immutable release.
5xxRepository manager/server issue.
  1. Check repository manager logs.

  2. If multi-module release partially deployed, stop and follow repository rollback/drop process.

Do not rerun blindly.

Rerun can make repository state worse.


39. CI/CD Maturity Ladder

LevelBehavior
0Developers manually run Maven; no standard CI.
1CI runs mvn clean install; unstable but useful.
2CI runs mvn clean verify; tests/reports visible.
3Dependency cache, pinned JDK/Maven, repository manager, settings discipline.
4PR/main/release pipelines separated; snapshots/releases governed.
5Staging/promotion, SBOM, scans, clean-room verification, provenance.
6Platform templates, policy-as-code, reproducible builds, fast affected-module builds.

The goal is not “more YAML”.

The goal is trustworthy artifact flow.


40. Final Mental Model

A Maven CI/CD pipeline is a control system over artifact state.

code -> verified build -> candidate artifact -> approved artifact -> deployed system

Every transition should be explicit.

Every artifact should be traceable.

Every remote publication should be intentional.

Every shortcut should be named and justified.

The highest-leverage Maven pipeline rule:

Use mvn verify to decide whether code is good enough, and use mvn deploy only when the organization is ready to share the artifact.


References

Lesson Recap

You just completed lesson 32 in deepen practice. Use the series map if you want to review the broader track, or continue directly into the next lesson while the context is still warm.

Continue The Track

Keep the momentum while the lesson is still fresh. Move backward for review or continue forward into the next concept.