skip to content

Multi-Project Builds

Structuring a build across many projects: include(), project dependencies, sharing logic with convention plugins, and composite builds. Interviewers ask because the old subprojects {} habit is exactly what modern Gradle wants you to unlearn.

on this pageshow

explore

questions

100 · 4 sections

What is a Gradle project path, and how does it differ from a project name?

level: juniorimportance: must knowfreq 55%
basics
~10 s

A project path is a colon-separated identifier like ':services:auth' that uniquely locates a project in the build tree. The name is just the last segment ('auth'); the path encodes its full position.

open as a page

How do you register subprojects in a Gradle multi-project build, and which file does that belong in?

level: juniorimportance: must knowfreq 75%
basics
~10 s

In settings.gradle.kts you call include(":app", ":lib"). Each path becomes a project in the build. Subprojects are NOT declared in build.gradle.kts — only the settings file defines which projects exist.

open as a page

What does customizing a subproject's projectDir in settings.gradle.kts let you do, and how do you set it?

level: juniorimportance: must knowfreq 45%
basics
~10 s

It decouples a project's logical path (like ':lib') from its folder on disk. In settings.gradle.kts you write project(":lib").projectDir = file("libs/lib") so ':lib' lives in a different directory than the default ./lib.

open as a page

In a Gradle multi-project build, what is the difference in responsibility between settings.gradle.kts and build.gradle.kts?

level: juniorimportance: must knowfreq 80%
basics
~10 s

settings.gradle.kts defines the build's structure — which projects exist (via include). build.gradle.kts defines what a single project does — its plugins, dependencies, and tasks.

open as a page

In a Gradle multi-project build, what determines the order in which projects are configured (evaluated), and is that order guaranteed?

level: middleimportance: must knowfreq 55%
basics
~10 s

Gradle configures projects in an order it chooses; by default the root is evaluated first, but the order among other projects is not something you should rely on unless you force it.

open as a page

In a Gradle multi-project build, how do you make one subproject depend on another subproject so it can use its classes and outputs?

level: juniorimportance: must knowfreq 80%
basics
~10 s

Add a project dependency in the consumer's build file, e.g. implementation(project(":lib")). Gradle builds :lib first and puts its compiled output and dependencies on the consumer's classpath.

open as a page

When :app declares implementation(project(':lib')), how does Gradle decide the order in which :lib and :app tasks run?

level: juniorimportance: must knowfreq 70%
basics
~10 s

Because :app's compile classpath needs :lib's jar, Gradle automatically builds :lib first. The project dependency creates implicit task dependencies, so :lib:jar runs before :app:compileJava — you don't wire that ordering by hand.

open as a page

How do you share test helper code (fixtures) between two Gradle modules so that one project's tests can reuse fixtures defined in another?

level: juniorimportance: must knowfreq 55%
basics
~10 s

Apply the java-test-fixtures plugin to the producing module, put shared helpers in src/testFixtures/java, then in the consumer declare testImplementation(testFixtures(project(":lib"))).

open as a page

How do you depend on a *specific* named configuration of another project in the same build, rather than its default API/runtime, and what does the syntax look like?

level: middleimportance: must knowfreq 45%
basics
~10 s

Use the two-arg project notation: project(path: ':lib', configuration: 'someConfig'). The configuration name selects which producer configuration to consume instead of the default one.

open as a page

When wiring one subproject onto another with project(...), what is the difference between declaring it with api(project(':lib')) versus implementation(project(':lib'))?

level: middleimportance: must knowfreq 75%
basics
~10 s

implementation(project(":lib")) keeps :lib private to the consumer — downstream projects can't see it. api(project(":lib")) exposes :lib transitively to anyone depending on the consumer. api needs the java-library plugin.

open as a page

What do the allprojects{} and subprojects{} blocks do when placed in a root build.gradle.kts, and how do they differ?

level: juniorimportance: must knowfreq 70%
basics
~10 s

Both apply configuration to many projects at once from the root build. allprojects{} includes the root plus every subproject; subprojects{} applies only to the child projects, excluding the root.

open as a page

What is the buildSrc directory in a Gradle build, and how does Gradle treat it?

level: juniorimportance: must knowfreq 70%
basics
~10 s

buildSrc is a special directory Gradle automatically compiles into a project and puts on every build script's classpath. You put shared build logic there — custom plugins, tasks, helper classes — without publishing anything.

open as a page

What does it mean to apply a convention plugin to a subproject, and how does it differ from configuring that subproject via subprojects {} in the root build?

level: juniorimportance: must knowfreq 60%
basics
~10 s

Each subproject opts in to shared config by listing the convention plugin in its own plugins {} block, e.g. id("myproject.java-conventions"). subprojects {} instead pushes config down from the root onto every subproject implicitly.

open as a page

Show how you would use subprojects{} to apply a common plugin, repositories, and a shared test dependency across all modules, and explain the configuration-name gotcha.

level: middleimportance: must knowfreq 60%
basics
~10 s

Put apply(plugin = "..."), a repositories {} block, and a dependencies {} block inside subprojects {}. Reference configurations like implementation by their string name because typed accessors aren't generated in the root script.

open as a page

What is an included 'build-logic' build, and how does it differ from buildSrc for holding shared build logic?

level: middleimportance: must knowfreq 55%
basics
~10 s

build-logic is a separate Gradle build (a folder with its own settings.gradle) wired in via includeBuild('build-logic'). It holds convention plugins like buildSrc but is an explicit, isolated build instead of an implicit one.

open as a page

In a Gradle composite build, what happens to a dependency on a published module when you include the producing build with includeBuild?

level: juniorimportance: must knowfreq 60%
basics
~10 s

Gradle automatically substitutes the published module coordinates with the local included build's project. Instead of downloading the artifact from a repository, it builds and uses the local source.

open as a page

What is a composite build in Gradle, and how do you wire one using includeBuild?

level: juniorimportance: must knowfreq 55%
basics
~10 s

A composite build combines otherwise independent Gradle builds into one. You wire it by adding includeBuild('../other-build') to settings.gradle.kts; Gradle then treats the included build as part of the current build.

open as a page

What must the in-development plugin build declare so that a consumer including it can apply the plugin by its id?

level: juniorimportance: must knowfreq 40%
basics
~10 s

The plugin build must apply the java-gradle-plugin plugin and declare the plugin id and implementationClass in a gradlePlugin { plugins { register(...) } } block. That generates the marker the consumer's id resolves against.

open as a page

How do you run a task that lives in an included build from the command line in a Gradle composite build?

level: juniorimportance: must knowfreq 55%
basics
~10 s

Prefix the task path with the included build's name: ./gradlew :includedBuildName:some:task. The leading segment is the build name (its directory name unless overridden), not a project in the root build.

open as a page

When automatic substitution in a composite build isn't enough, how do you declare an explicit substitution, and what is the syntax?

level: middleimportance: must knowfreq 50%
basics
~10 s

Use a dependencySubstitution block on the includeBuild and call substitute(module("group:name")).using(project(":path")) to map a published module to a specific project in the included build.

open as a page