After applying a shared convention plugin, a single module needs a slightly different setting (say a higher Java toolchain). How do you handle that override cleanly?
answer
- apply plugin, then re-set the property
- last write wins on the extension
- Property.set(...) overrides convention
- recurring deviation -> new plugin
- no root conditionals needed
basics
~20 sApply the convention plugin as usual, then add a normal configuration block in that one module's build script that resets the specific value — e.g. set java.toolchain to a higher version after the plugin runs.
solid answer
~40 sBecause applying a convention plugin still leaves the module's own `build.gradle.kts` in control, per-module overrides are natural: apply the plugin, then reconfigure the specific extension afterward in that module only. For a toolchain bump: ```kotlin plugins { id("myproject.java-conventions") } java { toolchain { languageVersion.set(JavaLanguageVersion.of(25)) } } ``` The later block in the script runs after the plugin's `apply`, so it wins for that property. Keep overrides small and local; if many modules need the same deviation, that's a signal to introduce a *second* convention plugin (e.g. `java25-conventions`) rather than scattering copies. The point is that opt-in application composes cleanly with local tweaks — unlike `subprojects {}`, where overriding for one module means adding awkward conditionals in the root block.
code
kotlin · 10 linesplugins {
id("myproject.java-conventions")
}
// override just for this module
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(25))
}
}go deeper
Know that you can re-configure a setting in the module after applying the plugin.
Explain last-write-wins / Property.set ordering and when to promote an override to its own plugin.
Reason about lazy Provider/Property semantics and keeping overrides from drifting into copy-paste.
Set policy on when deviations warrant a new shared plugin vs. inline override to keep the convention catalog coherent.
## Override is the normal case, not an exception Applying a convention plugin does not lock the module. The convention plugin runs its `apply` logic, then the rest of the module's `build.gradle.kts` continues to execute. Any configuration you write *after* (textually, in the same script) can adjust what the plugin set up. For most extension-style config, **last write wins**, so re-setting a property overrides the convention default. ## Example: bumping the toolchain for one module ```kotlin plugins { id("myproject.java-conventions") // sets toolchain to, say, 21 } // this module alone needs 25 java { toolchain { languageVersion.set(JavaLanguageVersion.of(25)) } } ``` The `java { }` block re-opens the same extension the plugin configured and overwrites `languageVersion`. Nothing about the convention plugin needs to change. ## When the value is a lazy Property/Provider Modern Gradle config often uses `Property<T>` (e.g. `languageVersion` is a `Property`). Calling `.set(...)` again simply replaces the value, and because it is lazy, the final value is read at execution time — so a later override still takes effect. Avoid `.convention(...)` for the override if the plugin already set an explicit value; `.set(...)` is the unambiguous way to win. ## Deciding override vs. new plugin - **One-off deviation** → inline override in that module. Cheap, local, readable. - **Recurring deviation across several modules** → don't copy the override into each script. Introduce a *separate* convention plugin (e.g. `myproject.java25-conventions`) and apply it where needed. This keeps the deviation itself shared and named. ## Contrast with subprojects {} Under a root `subprojects {}` block, carving out one module means sprinkling `if (project.name == "x")` conditionals into the root — fragile and non-local. With per-module application, the override lives exactly where the deviation is, and the rest of the build is untouched. This is a direct readability/maintainability advantage of the opt-in model. ## Summary Apply the shared preset, then locally re-set the specific property in the one module that differs; promote recurring deviations to their own convention plugin. The override mechanism is just ordinary script config running after the plugin.
- Why does the override block win over the convention plugin's value?Because it runs after the plugin's apply in the same script and re-sets the same extension property; for last-write-wins config (and lazy Property.set), the later assignment is the final value.
- Three modules now need the same override — what's the cleaner move?Stop copying the inline override. Author a separate convention plugin capturing the deviation (e.g. java25-conventions) and apply it to those three modules so the deviation itself is shared and named.
saying these in an interview costs you the question
- Editing the shared convention plugin to satisfy one module's special case.
- Adding if (project.name == ...) conditionals to a root block instead of overriding locally.