Chapter 29Lesson 03~240 minutes

CI/CD Build Patterns, Caching, Test Sharding, Artifact Promotion, and Ephemeral Build Agents: Configuration, Design Choices, and Tradeoffs

Choose CI build controls by the state they affect: dependency caches versus build caches, one job versus shards, persistent versus ephemeral agents, rebuild-per-environment versus immutable promotion, and provider YAML versus portable repository-owned scripts.

Dependency cacheBuild cacheShard designAgent modelPortability

Learning objectives

  • Choose dependency caches, build caches, or pipeline artifacts according to the state being reused.
  • Evaluate shard count against setup cost, determinism, report aggregation, and resource limits.
  • Compare persistent and ephemeral agents without assuming either is inherently secure or fast.
  • Reject rebuild-per-environment when immutable promotion can preserve artifact identity.
  • Keep portable build correctness in repository-owned scripts while limiting provider-specific YAML to orchestration.

Design rule. Every performance optimization must preserve source/toolchain identity, dependency trust, test evidence, and artifact checksum. If an optimization makes any of those harder to explain, its cost is larger than the saved seconds.

1. Dependency cache versus Build Cache

A dependency cache avoids redownloading external modules/plugins. A Build Cache reuses outputs of cacheable tasks whose modeled inputs produce the same cache key. They solve different bottlenecks and carry different poisoning risks. Restoring dependencies cannot legitimately make compileJava FROM-CACHE; a Build Cache can.

Choice Good fit Primary risk Evidence
Dependency cache Network-heavy dependency/plugin resolution. Repository metadata staleness or untrusted cached artifacts if verification is weak. Resolution logs, verification metadata, lockfiles.
Local Build Cache Repeated work on one developer/agent. Incorrect hits when task inputs are incomplete. Task outcome FROM-CACHE, cache-key diagnostics.
Remote Build Cache Trusted reusable task outputs across agents. Poisoning if write authority is too broad. Producer trust, cache key, task input model.
Pipeline artifact store Immutable handoff between stages. Wrong artifact/run selected. Run ID + checksum + source/tool identity.

2. One large job versus test shards

Sharding reduces wall-clock time only when independent test work is large enough to outweigh duplicate startup, checkout, dependency resolution, JVM startup, and report-merge cost. A two-minute suite may become slower when split across ten cold agents. A forty-minute suite with stable class-level partitions may benefit substantially.

Prefer deterministic assignments based on tags, test classes, historical timing data with a reviewed manifest, or a stable hash. Never define shards by nondeterministic filesystem enumeration without validating the final union.

3. Persistent versus ephemeral agents

Persistent agents can have warm local caches and long-lived processes, but they accumulate hidden state. Ephemeral agents make isolation easier to reason about but may pay more bootstrap cost. Neither model eliminates the need for Wrapper/JDK verification, secret scoping, or cache policy.

Agent model Benefit Cost Production guardrail
Persistent Warm caches/daemons; lower startup cost. State leakage, drift, harder incident cleanup. Immutable images where possible, explicit cache dirs, periodic clean-room validation.
Ephemeral Clear job boundary; easier reproducibility evidence. Download/bootstrap cost; cache service dependency. Verified Wrapper/JDK and explicitly restored approved caches.

4. Rebuild per environment versus immutable promotion

Rebuilding for dev, staging, and production can appear “clean,” but it destroys artifact identity. The correct isolation boundary is environment configuration and deployment authorization, not compilation. Build one candidate, test it, hash/sign it, and promote or copy that same object. If policy requires environment-specific assembly, then those assemblies are distinct artifacts and need distinct identities—they should not masquerade as the same release.

5. Provider YAML versus portable build scripts

CI YAML should orchestrate jobs, permissions, artifacts, caches, approvals, and runner placement. Build correctness—tasks, dependency policy, test suite composition, reproducible packaging—belongs in Maven/Gradle project configuration or repository-owned scripts. This keeps ./gradlew ciTest jar or an equivalent Maven command meaningful on a developer machine and another CI provider.

6. Trusted and untrusted pipeline classes

Pull requests from forks, protected branches, release tags, and scheduled dependency updates often have different trust levels. Design cache write authority, secret access, and publication rights accordingly. A useful policy is: untrusted jobs may read only carefully selected public/reviewed cache state; protected jobs may publish validated Build Cache entries and release artifacts. Exact CI features differ by platform, but the boundary remains.

7. Decision table for a mixed Maven/Gradle estate

Question Prefer Why / observable behavior
Downloads dominate build time? Dependency cache. Fewer remote requests while compilation/tests still execute normally.
Compilation/tests are cacheable and repeated across trusted agents? Remote Build Cache (Gradle) or equivalent controlled task-output strategy. Eligible tasks can report restored outcomes instead of rerunning.
Need release handoff between stages? Pipeline artifact/repository promotion. Checksum ties downstream stage to exact upstream bytes.
Test suite is large and independent? Deterministic shards. Wall time can fall while shard manifests/reports prove coverage.
Small suite with high agent startup cost? Single verification job. Avoid duplicate bootstrap and report aggregation overhead.
Teams use multiple CI vendors? Portable build scripts + thin provider adapters. Same Wrapper commands run locally and across CI implementations.

8. Worked scenario

A 20-module Gradle service takes 18 minutes: 3 minutes dependency download on cold agents, 12 minutes tests, 3 minutes compile/package. First, enable a verified dependency cache and measure cold/warm differences. Second, split the 12-minute suite into two balanced deterministic shards, preserving XML reports and a coverage manifest. Third, export the single packaged artifact and checksum. Do not add a broad cache containing build/ or rebuild the JAR in deployment merely because it seems simpler.

If later measurement shows compile tasks are stable/cacheable, introduce a protected remote Build Cache as a separate change. One variable at a time keeps performance causality visible.

9. Boundaries this chapter does not own

JDK image patching belongs to image/runner management; repository-manager retention and promotion rules belong to artifact-repository governance; branch protection and secret approval rules belong to CI-platform governance; deployment rollout belongs to delivery/platform courses. This chapter defines the build-tool contract those systems must preserve.

Knowledge check

Which cache can legitimately restore a Gradle task output?

When can sharding make CI slower?

Why is a persistent runner not automatically unsafe?

Why is a deployment-stage rebuild a new artifact identity?

What belongs in CI YAML rather than build.gradle.kts?

Official references and version notes

Version-sensitive statements were rechecked against primary documentation on 2026-08-24. Mandatory labs remain local/free; hosted CI, remote caches, artifact repositories, and secret stores are represented as optional production mappings only.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.