skip to content

What was the `<<` (leftShift) operator on tasks, why was it removed, and what replaces it?

level: middleimportance: nice to knowfreq 25%

answer

  1. << = leftShift = doLast shorthand
  2. Groovy DSL only
  3. removed in Gradle 5.0
  4. hid config-vs-action distinction
  5. replace with doLast {}

basics

~20 s

In old Groovy DSL, task foo << { ... } was shorthand for adding a doLast action. It was deprecated in Gradle 3.x and removed in Gradle 5.0 because it blurred configuration vs action code. Use doLast { } instead.

solid answer

~40 s

The `<<` (leftShift) operator was a Groovy-DSL shortcut: `task foo << { ... }` appended the closure as a `doLast` **action**. It looked convenient but caused a common bug — beginners assumed the closure body *was* the task body and put **configuration** code inside it, which then ran at execution time instead of configuration time, breaking ordering and dependencies. Because it obscured the crucial configuration-vs-action distinction, Gradle **deprecated** `<<` around 3.x and **removed** it in **Gradle 5.0**. The fix is explicit: write `tasks.register("foo") { doLast { ... } }`, putting wiring in the configuration block and work inside `doLast`. The removal was part of a broader push (lazy task configuration, the Provider API) to make the two phases unambiguous. Knowing this mainly matters when reading legacy Groovy build scripts.

code

groovy · 7 lines
groovy
// Old (removed in Gradle 5.0):
// task hello << { println 'Hello' }

// Modern replacement:
tasks.register('hello') {
    doLast { println 'Hello' }
}

go deeper

for a junior

Just recognize that << is old syntax replaced by doLast; details optional.

for a middle

Explain that << was a Groovy doLast shorthand, removed in Gradle 5.0, and why it confused config vs action.

for a senior

Use it to illustrate the broader move toward explicit, lazy task APIs and migration concerns.

for a principal

Frame around modernizing legacy Groovy build scripts during org-wide Gradle upgrades and enforcing current idioms.

## The old shorthand In early Groovy-DSL Gradle you could write: ```groovy task hello << { println 'Hello' } ``` The `<<` (leftShift) operator was overloaded on `Task` to mean 'append this closure as a `doLast` action'. So the above was equivalent to: ```groovy task hello { doLast { println 'Hello' } } ``` ## Why it was a footgun The operator hid the distinction between the **configuration block** and an **action**. A frequent mistake: ```groovy task build << { dependsOn someOther // WRONG: runs at execution time, too late to affect the graph } ``` Here `dependsOn` (a configuration concern) ends up inside a `doLast` action, executing during the execution phase when it can no longer influence task ordering. New users were repeatedly bitten because `<<` made the action block look like 'the task'. ## Deprecation and removal - **Deprecated**: Gradle 3.2 era. - **Removed**: **Gradle 5.0** — using `<<` is now a build error. ## What to use instead Be explicit and lazy: ```kotlin tasks.register("hello") { group = "demo" // configuration doLast { println("Hello") } // action } ``` This makes it obvious which code configures and which code does work, aligning with configuration avoidance (`register` over `create`) and the modern lazy APIs. In Kotlin DSL `<<` never existed at all; it was purely a Groovy artifact. ## Interview relevance Low on its own, but it signals you understand the configuration-vs-action split and can read/maintain legacy Groovy scripts during migrations.

  • What concrete bug did << encourage in beginners' scripts?
    Putting configuration code (like dependsOn or property setters) inside the action block, so it ran during the execution phase, too late to affect the task graph.
  • Did the Kotlin DSL ever support <<?
    No. leftShift was a Groovy-only operator overload on Task; the Kotlin DSL always required explicit doFirst/doLast.

saying these in an interview costs you the question

  • Claiming << still works in current Gradle.
  • Saying << added a doFirst action (it added doLast).
  • Thinking << was a Kotlin DSL feature.

context