skip to content

What do @Internal and @Optional do on task properties, and when do you reach for them?

level: middleimportance: should knowfreq 45%

answer

  1. @Internal = exclude from tracking
  2. @Optional = nullable tracked property
  3. @Optional is a modifier, never alone
  4. @Internal silences validation warning
  5. don't @Internal a real input

basics

~10 s

@Internal marks a property as deliberately NOT part of inputs/outputs, so it's excluded from up-to-date checks. @Optional says an input/output property is allowed to be unset/null without failing validation.

solid answer

~40 s

`@Internal` declares that a property is intentionally *not* a tracked input or output — useful for services, loggers, derived values, or anything that doesn't affect the result. It silences Gradle's property-validation warning ('property X is not annotated') and keeps the fingerprint clean. `@Optional` is a *modifier* you stack on an input/output annotation (e.g. `@Optional @InputFile`) to say the property may be absent/null. Without it, an `@InputFile` whose value is unset fails validation; with it, an unset value simply contributes nothing to the fingerprint. They solve different problems: `@Internal` = 'ignore this for incrementality'; `@Optional` = 'this tracked property might legitimately have no value'. A frequent mistake is using `@Internal` to suppress a warning on something that actually *does* affect output — that reintroduces the stale-output bug `@Internal` looks like it fixes.

code

kotlin · 8 lines
kotlin
abstract class DeployTask : DefaultTask() {
    @get:Input              abstract val env: Property<String>

    @get:Optional
    @get:InputFile          abstract val overrides: RegularFileProperty  // may be unset

    @get:Internal           abstract val clock: Property<Clock>          // not tracked
}

go deeper

for a junior

Know that @Internal excludes a property from up-to-date checks and @Optional allows an input/output to be unset.

for a middle

Explain the validation warning @Internal resolves and that @Optional is a modifier paired with another annotation.

for a senior

Reason about the correctness risk of mis-marking a real input as @Internal and how to audit task types for it.

for a principal

Define review guidance that every property be deliberately classified; treat unjustified @Internal as a build-correctness defect.

## @Internal — explicit exclusion Every property on a task type should be classified. Gradle's validation flags any public getter that is neither an input, an output, nor `@Internal`. `@Internal` is how you say: *'this property exists but does not influence the task's result, so do not fingerprint it.'* Typical `@Internal` candidates: - injected services / `BuildService` handles - a `Logger` - derived/convenience accessors computed from other inputs - things like a temporary working directory you don't want to track ```kotlin @get:Internal abstract val httpClient: Property<HttpClient> ``` If you `@Internal` something that *does* affect output, the task wrongly stays UP-TO-DATE — the same class of bug as forgetting an annotation entirely. ## @Optional — nullable tracked properties By default a declared input/output must have a value. An unset `@InputFile`, for instance, fails validation with 'does not have a configured value'. `@Optional` relaxes that: ```kotlin @get:Optional @get:InputFile abstract val overrideConfig: RegularFileProperty ``` Now `overrideConfig` may be left unset; when unset it simply doesn't contribute to the fingerprint, and when set it's tracked normally. `@Optional` is a *combinable modifier* — it does nothing on its own and is always paired with an input/output annotation. ## Side-by-side | | @Internal | @Optional | |---|---|---| | Role | exclude from tracking | allow null/unset on a tracked property | | Used alone? | yes | no (modifier) | | Affects fingerprint? | removes property entirely | property tracked only when present | ## Mental model - Reach for `@Internal` when the answer to 'does changing this change my output?' is *no*. - Reach for `@Optional` when the answer is *yes, but it's allowed to be absent*.

  • Can @Optional be used without an input/output annotation?
    No. @Optional is a modifier with no meaning on its own; it must accompany an annotation like @InputFile, @InputDirectory, or @OutputFile to relax that property's required-value rule.
  • What is the risk of over-using @Internal to clear validation warnings?
    If you mark a property @Internal that actually influences the output, Gradle stops fingerprinting it and the task can stay UP-TO-DATE despite a meaningful change — a silent stale-output bug masquerading as a clean build.

saying these in an interview costs you the question

  • Saying @Internal and @Optional are interchangeable — one excludes tracking, the other relaxes a tracked property's nullability.
  • Using @Internal as a generic 'make the warning go away' switch regardless of whether the property affects output.

context