skip to content

How does moving convention plugins into a build-logic build let you publish and share them across repositories, and what does that enable that buildSrc cannot?

level: seniorimportance: should knowfreq 35%

answer

  1. build-logic = real plugin projects
  2. maven-publish → versioned coordinates
  3. consume via pluginManagement + id + version
  4. versioned build platform / governance
  5. buildSrc is local-only, no coordinates

basics

~20 s

Because build-logic is a real build with java-gradle-plugin projects, you can publish its convention plugins to a Maven repo and apply them from other repos by id. buildSrc is local-only and can't be published or reused outside its build.

solid answer

~50 s

`buildSrc` is bound to a single build: its plugins are only ever on that build's script classpath and can't be consumed elsewhere. A `build-logic` build is composed of normal Gradle plugin projects (using `java-gradle-plugin`/`kotlin-dsl` with the plugin-publish or maven-publish machinery), so the very same convention plugins can be published to an internal Maven repository with a group, artifact, and version. Other repositories then add the repository to their `settings.gradle.kts` `pluginManagement` block and apply the plugins by id — `plugins { id("com.acme.java-conventions") version "1.4.0" }`. This turns your build conventions into a versioned, governed **build platform**: many teams share one source of truth for toolchains, quality gates, and publishing config; upgrades roll out by bumping a version; and the plugins are independently unit-tested with TestKit. You typically keep `includeBuild` for local iteration in the platform repo and switch consumers to the published coordinates.

code

kotlin · 14 lines
kotlin
// build-logic/java-conventions/build.gradle.kts
plugins { `kotlin-dsl`; `maven-publish` }
group = "com.acme.build"
version = "1.4.0"
publishing {
  repositories { maven { url = uri("https://repo.acme.internal/build-plugins") } }
}

// consumer-repo/settings.gradle.kts
pluginManagement {
  repositories { maven { url = uri("https://repo.acme.internal/build-plugins") }; gradlePluginPortal() }
}
// consumer-repo/app/build.gradle.kts
plugins { id("com.acme.java-conventions") version "1.4.0" }

go deeper

for a junior

Know build-logic plugins can be published and reused, buildSrc cannot.

for a middle

Show the maven-publish setup and the consumer's plugins { id() version() } usage.

for a senior

Explain the versioned-build-platform model, pluginManagement wiring, and TestKit testing of plugins before publish.

for a principal

Frame governance: one owning team, version-bump rollout of security/toolchain changes across many repos, and local-vs-published duality for platform development.

## buildSrc is a dead end for sharing `buildSrc` exists only inside one build. There is no coordinate, no version, no artifact — its compiled output is injected onto that build's script classpath and nowhere else. If three repositories all need the same `java-conventions`, buildSrc forces you to copy the source into each. That's exactly the cross-repo duplication a build platform is meant to kill. ## build-logic is just plugins, so it can be published Inside `build-logic` you write ordinary Gradle plugins: ```kotlin // build-logic/java-conventions/build.gradle.kts plugins { `kotlin-dsl` `maven-publish` } group = "com.acme.build" version = "1.4.0" publishing { repositories { maven { url = uri("https://repo.acme.internal/build-plugins") } } } ``` Precompiled script plugins automatically register a plugin id derived from the file name (e.g. `com.acme.java-conventions.gradle.kts` → id `com.acme.java-conventions`). Publishing produces a normal plugin marker artifact plus the implementation jar. ## Consuming the published plugins from another repo ```kotlin // other-repo/settings.gradle.kts pluginManagement { repositories { maven { url = uri("https://repo.acme.internal/build-plugins") } gradlePluginPortal() } } ``` ```kotlin // other-repo/app/build.gradle.kts plugins { id("com.acme.java-conventions") version "1.4.0" } ``` ## What this enables - **A versioned build platform.** Conventions (toolchain version, detekt/spotless config, publishing rules) become a product with a changelog; consumers upgrade by version. - **Governance & consistency.** One team owns the conventions; security or compliance changes ship everywhere via a version bump. - **Independent testing.** Each plugin project is unit-testable with the Gradle TestKit `GradleRunner`, so you validate conventions before publishing. - **Local vs published duality.** In the platform repo you keep `includeBuild("build-logic")` for fast iteration; downstream repos consume the published coordinates. Some orgs use composite builds to swap to a local checkout while developing the platform. buildSrc gives none of this: no version, no publication, no cross-repo reuse, and weaker testability.

  • Where do consumers declare the repository hosting the published plugins?
    In the pluginManagement { repositories { } } block of the consuming build's settings.gradle.kts, so the plugins block can resolve the id and version.
  • How do you keep fast local iteration on the platform itself?
    Keep includeBuild("build-logic") within the platform repo for instant feedback, and publish versioned artifacts for downstream consumers; you can even includeBuild a local checkout to override the published version during development.
  • Why can't buildSrc be reused across repos?
    It has no coordinates, version, or publication — it's compiled only onto its own build's classpath, so the only way to reuse it is to copy the source.

buildSrc is a recipe written on the kitchen wall of one restaurant. build-logic publishes the cookbook with an edition number so every branch cooks the same dish and upgrades together.

saying these in an interview costs you the question

  • Claiming you can publish buildSrc — you cannot; it has no artifact coordinates.
  • Forgetting that consumers need pluginManagement repositories to resolve published convention plugins by id.

context