Chapter 17Lesson 03~165 minutes

Gradle Tasks, Task Graph, Dependencies, Ordering, Inputs, Outputs, and Incremental Work: Configuration, Design Choices, and Tradeoffs

Choose deliberately between dependency and ordering edges, ad-hoc and typed tasks, coarse and precise outputs, and simple up-to-date work versus true incremental processing.

OrderingTyped tasksOutput ownershipIncrementalityTradeoffs

Choose deliberately between dependency and ordering edges, ad-hoc and typed tasks, coarse and precise outputs, and simple up-to-date work versus true incremental processing.

Learning objectives

  • Choose a dependency edge when correctness requires another task and an ordering-only rule when selection must remain independent.
  • Compare ad-hoc task actions with reusable typed task classes and Provider-backed properties.
  • Choose precise output ownership boundaries that enable reliable validation and parallelism.
  • Decide when file-level incremental implementation complexity is justified.
  • Evaluate portability, reproducibility, CI throughput, maintainability, and upgrade cost for each task-design choice.
  • Use a worked decision table to justify a production task model from observable behavior.

1. Design starts with correctness semantics, not the shortest DSL

Task APIs are expressive enough to model several superficially similar relationships. Production builds become fragile when the author chooses the construct with the shortest syntax rather than the one whose semantics match the data flow.

Use this decision sequence: What output/result is required? Who owns it? What exact state affects it? Does another task have to be selected? Can the work be repeated from scratch? Does workspace location matter?

2. Dependency edge versus ordering-only rule

Choose dependsOn when B is not correct unless A has run or its modeled output is available. Choose mustRunAfter when A and B are independently meaningful but cannot safely overlap/order arbitrarily when co-selected. Use shouldRunAfter only when the order is a preference rather than a correctness condition.

Scenario Correct model Why
Report task consumes file generated by test aggregation Dependency/data-flow edge The report cannot exist correctly without its producer.
Two destructive maintenance tasks are independently runnable but should not overlap mustRunAfter plus explicit shared-resource policy Each can be selected alone; order matters only when both run.
Fast validation is preferred before slow integration tests shouldRunAfter where appropriate Preference may yield to parallel progress; correctness must not depend on it.
Temporary service must stop after integration test even on failure finalizedBy Cleanup must be selected with the finalized work and run after it.

3. Ad-hoc doLast task versus typed task

An ad-hoc registered task is excellent for small, repository-local orchestration. A typed task becomes preferable when the behavior is reused, has several typed inputs/outputs, needs validation annotations, or implements file-level incrementality.

Choice Strengths Costs / risks Good fit
Ad-hoc registered task Small amount of code; easy to read near usage. Runtime API declarations can drift from action code; weaker reusable API surface. Tiny repository-specific glue.
Typed DefaultTask Typed lazy properties, annotations, reusable behavior, clearer validation. More code and API design responsibility. Reusable transformation, complex state model, plugin/convention logic.
External shell command hidden in doLast Quick experiment. Portability, quoting, process/environment inputs, exit semantics, and cacheability are easy to model incorrectly. Only when tool boundary is intentional and fully modeled.

Typed does not automatically mean correct. A typed task can still read undeclared state or write overlapping outputs. The annotations/properties must reflect actual behavior.

4. Coarse output directory versus precise output ownership

Declaring the entire build/generated directory as the output of several unrelated tasks creates overlapping ownership. Gradle cannot safely reason about which task produced which files, and cache reuse/parallel execution become harder or unsafe.

Prefer one owner per output location. If tasks collaborate, give each its own subdirectory or model producer outputs as inputs to an aggregation task.

Good ownership:
build/generated/schema/      <- generateSchema owns it
build/generated/client/      <- generateClient owns it
build/reports/aggregate.txt  <- aggregateReport owns it

Risky ownership:
build/generated/             <- generateSchema + generateClient both claim/write it

5. Simple rerun versus true incremental transformation

Do not add InputChanges because “incremental sounds faster.” First measure the task. If it processes ten tiny files in milliseconds, a clear full rebuild may cost less than maintaining removal logic, path normalization, and partial-output correctness.

Signal Prefer ordinary tracked task Consider true incremental task
Input size Small/few files. Large many-file input set.
Per-file cost Cheap transformation. Expensive independent per-file work.
Output mapping Single aggregate artifact. Mostly one-to-one or cleanly partitioned outputs.
Removal handling Full rebuild naturally removes stale state. Task can deterministically remove outputs for removed inputs.
Team complexity budget Simplicity favored. Measured CI/developer savings justify more task code.

6. Path sensitivity is a semantic contract, not a performance toggle

ABSOLUTE can make identical content appear different after workspace relocation. NONE can make different filenames appear equivalent when they should not. Pick the weakest sensitivity that is still semantically true—not the weakest one that produces a desired cache result.

For a directory transformer that preserves relative paths, RELATIVE is usually a natural model: docs/a.txt and src/a.txt are distinct within the root, while /agent-1/work/repo versus /agent-2/work/repo is irrelevant.

7. Distinguish task state from other build/platform state

State/control Owned by Not solved by task ordering
Gradle runtime JVM Wrapper/Gradle runtime environment A task edge cannot make an unsupported JVM compatible.
Dependency repository policy settings/repository management Task ordering cannot validate repository provenance.
CI secret injection CI/environment credential boundary Declaring an input does not make secret logging safe.
Project task graph Build logic/plugins This chapter’s dependency/order/input/output model.
Gradle User Home caches Gradle runtime state Deleting them is not a substitute for fixing undeclared task inputs.
IDE behavior IDE Gradle integration IDE execution must still use the repository’s wrapper/task model.

8. Worked decision: code generator in CI

A team has generateApi, compileJava, and lintGenerated. The generator consumes schema/*.yaml and writes Java files. Compilation needs generated Java. Lint is useful but can run independently on an existing generated tree.

Question Decision Observable consequence
Does compile require generation? Wire generator output into compile input/source set; dependency follows data flow. Selecting compile/build includes generation when required.
Should lint force generation? Only if lint’s correctness contract requires freshly generated output; otherwise keep independent and document precondition. Selecting lint alone either remains independent or deliberately adds generation.
Output location? Dedicated build/generated/api. Single ownership; source tree stays immutable.
Path sensitivity? RELATIVE for schema directory if relative schema paths are semantic. Workspace relocation does not invalidate solely due to absolute root.
Incremental? Measure first; implement only if generator can safely process file changes/removals. Optimization follows evidence, not fashion.

9. Tradeoff matrix for a production review

Choice Maintainability Reproducibility/security CI throughput Upgrade cost
Strong data-flow dependencies Graph intent is explicit. Clean agents select required work. May execute more work, but only necessary prerequisites if graph is accurate. Usually low; relies on public task APIs.
Ordering-only rules Useful when selection independence is real. Risky if misused to hide missing dependency. Can preserve independent execution/parallelism. Low syntax cost, high semantic review importance.
Typed task + precise properties Clear reusable contract. Better validation and state visibility. Supports reliable up-to-date/incremental optimization. Task API becomes maintained build code.
Coarse shared outputs Initially simple. Ownership ambiguity and stale/corrupt state risk. Hurts parallelism/cache reliability. Expensive to untangle later.
True incremental work More implementation/test burden. Must handle removals and non-incremental rebuild correctly. Can materially reduce large transformation cost. Requires stronger compatibility tests.

10. Production rule: optimize the model you can explain

A reviewable task graph lets CI choose a smaller correct scope, reason about parallelism, and reuse work safely. If a team cannot explain why a task was skipped or rerun using declared state, the optimization is too opaque or the model is incomplete.

Knowledge check

A task reads another task’s output. Is mustRunAfter enough?

Why can one giant output directory be harmful?

When is a typed task worth the extra code?

Should you choose PathSensitivity.NONE for maximum reuse?

When should you implement InputChanges?

What should happen before adding an ordering rule to “fix” CI?

Official references and version notes

Version snapshot: Generated for August 24, 2026 with Gradle 9.7.1, JDK 21 as the Gradle runtime, and Java 17 as the course JVM target. Re-check current Gradle documentation before carrying version-sensitive behavior into future production builds.

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.