skip to content

When developing a plugin together with its consumer, when would you prefer an included build (includeBuild) over putting the plugin in buildSrc?

level: seniorimportance: should knowfreq 35%

answer

  1. buildSrc = implicit, private, all-classpath
  2. includeBuild = explicit, shareable
  3. real id resolution path
  4. reconfiguration cost
  5. build-logic migration

basics

~10 s

Use buildSrc for plugin logic private to one build. Use an explicit included build when the plugin is reusable, applied by a public id, or shared across multiple consumer builds.

solid answer

~50 s

`buildSrc` is an implicit included build that's compiled before everything else and put on every project's build classpath automatically. It's great for build logic private to a single build, but any change forces full reconfiguration, you can't apply its plugins via a real `plugins { id(...) }` id, and it can't be shared with another build. An explicit `includeBuild("build-logic")` makes the plugin a standalone, shareable build that the consumer applies by its **published plugin id** — exactly the path real users take. It only rebuilds when the consumer actually uses it, can be included by several different consumers, and lets you test the true id-based consumption. So: prototyping internal logic → buildSrc; iterating a plugin you intend to publish, or sharing one plugin build across multiple consumers → includeBuild. Many teams migrate from buildSrc to a `build-logic` included build for exactly this reason.

go deeper

for a junior

Know buildSrc auto-loads build logic for one build; includeBuild wires in another local build explicitly.

for a middle

Explain that buildSrc is private and on every classpath, while includeBuild is shareable and applied by real id.

for a senior

Reason about reconfiguration cost, the true id-resolution path, and when to choose each for plugin-and-consumer co-development.

for a principal

Frame the buildSrc→build-logic migration as a scaling/governance decision for shared build logic across a monorepo or many repos.

## Two ways to keep plugin source local ### buildSrc `buildSrc` is a magic directory: if it exists, Gradle treats it as an **implicit included build**, compiles it before the main build, and places its output on the build script classpath of **every** project automatically. You don't `includeBuild` it and you don't apply it by id — its classes/plugins are just available. - Pros: zero wiring; convention plugins (`src/main/kotlin/my-convention.gradle.kts`) are trivial. - Cons: any edit invalidates the whole build's configuration (everything depends on it); it's tied to **one** build (not shareable); and you can't exercise the public `plugins { id("...") }` resolution path because it's always on the classpath regardless of id. ### Explicit included build (`includeBuild("build-logic")`) You declare `includeBuild("build-logic")` in the consumer's `settings.gradle(.kts)`. The included build is a complete, standalone Gradle build that applies `java-gradle-plugin` and declares its plugin id. The consumer applies it via the **real id**, and Gradle substitutes the plugin marker to the included build. - Pros: shareable across many consumers; only rebuilt when actually used; exercises the **true consumption path** (id resolution + marker substitution); cleaner separation; better for plugins you intend to publish. - Cons: slightly more wiring; the included build is independent (its own version catalog, settings plugins, etc.). ## Decision guide | Need | Prefer | |------|--------| | Private convention logic for one build, quick | `buildSrc` | | Plugin you'll publish, want to test id-based apply | `includeBuild` | | One plugin build shared by several consumer builds | `includeBuild` | | Avoid full reconfiguration on every plugin edit | `includeBuild` | ## Migration note Gradle's own guidance nudges teams toward an explicit `build-logic` included build over a large `buildSrc`, because it scales better, is shareable, and lets you apply convention/published plugins by id. You can mix: `build-logic` holds shared convention plugins, included via `includeBuild`. ## Subtlety: applying by id from an included build Because `includeBuild` substitutes the marker, the consumer's `plugins { id("com.example.myplugin") }` resolves to your local source — the *same* code path a downstream user hits after you publish. buildSrc can't reproduce that, which is why it's weaker for plugin-and-consumer co-development specifically.

  • Why does a change in buildSrc tend to be more disruptive than a change in an included build?
    buildSrc output is on every project's build classpath, so editing it invalidates the configuration of the entire build and forces re-compilation/reconfiguration broadly. An included build is only rebuilt when a consumer task actually depends on its output, so unrelated work isn't invalidated.
  • Can you have both buildSrc and an included build-logic build in the same workspace?
    Yes. They coexist: buildSrc auto-loads for the current build, while includeBuild wires in a separate, shareable build. Teams often keep shared convention/published plugins in build-logic and reserve buildSrc for small local helpers, or migrate fully to build-logic.

buildSrc is like a kitchen drawer bolted into one house — always there, but you can't lend it out. An included build is a portable toolbox you can carry to any house and that you only open when you actually need a tool.

saying these in an interview costs you the question

  • Claiming buildSrc and includeBuild are interchangeable in all cases.
  • Saying you can apply a buildSrc plugin via a published plugin id and marker substitution — buildSrc is just on the classpath.
  • Asserting an included build always rebuilds even when unused; it rebuilds only when its output is needed.

context