When developing a plugin together with its consumer, when would you prefer an included build (includeBuild) over putting the plugin in buildSrc?
answer
- buildSrc = implicit, private, all-classpath
- includeBuild = explicit, shareable
- real id resolution path
- reconfiguration cost
- build-logic migration
basics
~10 sUse 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
Know buildSrc auto-loads build logic for one build; includeBuild wires in another local build explicitly.
Explain that buildSrc is private and on every classpath, while includeBuild is shareable and applied by real id.
Reason about reconfiguration cost, the true id-resolution path, and when to choose each for plugin-and-consumer co-development.
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.