skip to content

What does includeFlat('sibling-project') do in a Gradle settings file, and how does the resulting project layout differ from a normal include?

level: middleimportance: should knowfreq 35%

answer

  1. sibling dir = ../name
  2. resolves vs rootDir.parent
  3. still one root, one Settings
  4. logical path :name, only projectDir differs
  5. not a composite build

basics

~10 s

includeFlat adds a project that lives in a sibling directory next to the root project (../sibling), instead of beneath the root like include does.

solid answer

~40 s

`includeFlat('sibling-project')` is a `Settings` API call (in `settings.gradle.kts`) that registers a subproject whose directory is `../sibling-project` — a sibling of the root project's directory, not a child of it. A normal `include('sub')` expects the project under the root tree (`rootDir/sub`). With `includeFlat`, the build still has one root project and one `Settings`, but the participating projects sit side-by-side on disk. This 'flat' layout is useful when several independently-cloned repos share a build, or when you don't want the build to own a deeply nested directory structure. The project still appears under the root in the project hierarchy (path `:sibling-project`); only its `projectDir` differs from the default. Path resolution is the key difference: `include` resolves relative to `rootDir`, `includeFlat` resolves relative to `rootDir`'s parent.

code

kotlin · 9 lines
kotlin
// workspace/my-app/settings.gradle.kts
rootProject.name = "my-app"

include("core")             // projectDir = my-app/core
includeFlat("shared-lib")   // projectDir = ../shared-lib  (workspace/shared-lib)

// Equivalent long form of includeFlat:
// include("shared-lib")
// project(":shared-lib").projectDir = file("../shared-lib")

go deeper

for a junior

Know that includeFlat points at a sibling directory (../name) instead of a subdirectory.

for a middle

Explain the path-resolution difference (rootDir.parent vs rootDir) and that the logical project path is still a direct child of root.

for a senior

Contrast it with composite builds and articulate when side-by-side checkouts justify it over hierarchical include.

for a principal

Weigh flat layout against monorepo/composite strategies for org-wide repo topology, tooling, and CI checkout conventions.

## The problem flat layouts solve Gradle's default multi-project layout is **hierarchical**: the root project owns a directory, and every subproject lives in a subdirectory beneath it. You declare those subprojects in `settings.gradle.kts` with `include`. But sometimes the projects you want to build together are **siblings on disk** — for example each is its own git clone in a common parent folder: ``` workspace/ my-app/ <- root project (settings.gradle.kts here) shared-lib/ <- a sibling, separate checkout ``` A `flat layout` lets you wire `shared-lib` into `my-app`'s build without moving it under `my-app/`. ## `include` vs `includeFlat` Both are methods on the `Settings` object (the `this` inside `settings.gradle.kts`). - `include("sub")` registers a project whose default `projectDir` is `${rootDir}/sub` — **under** the root. - `includeFlat("sibling")` registers a project whose `projectDir` is `${rootDir.parentFile}/sibling` — i.e. `../sibling`, a **sibling** of the root directory. In both cases the project's *logical* path in the build is a direct child of the root: `:sibling`. Only the physical directory differs. `includeFlat` is essentially a convenience for: ```kotlin include("sibling") project(":sibling").projectDir = file("../sibling") ``` ## What it does NOT do - It does **not** create a composite build. There is still exactly one `Settings`/one root project; the sibling is a plain subproject, not a separate included build (that's `includeBuild`). - It does **not** support nested paths — you pass a single directory name, and the project becomes a top-level child of the root. You cannot say `includeFlat("a:b")`. ## When to reach for it Use it when external/sibling checkouts must share a single build and you control the parent directory layout. Most modern monorepos prefer hierarchical `include` (everything under one root) or, for truly independent builds, **composite builds** (`includeBuild`). `includeFlat` is the middle ground for the classic 'side-by-side checkouts' workflow. ```kotlin // settings.gradle.kts in workspace/my-app rootProject.name = "my-app" include("core") // -> my-app/core includeFlat("shared-lib") // -> workspace/shared-lib (../shared-lib) ```

  • Where on disk does Gradle look for an includeFlat('x') project?
    In the root project's parent directory: `${rootDir.parentFile}/x`, i.e. `../x` relative to the root project.
  • Is includeFlat the same as includeBuild?
    No. includeFlat adds a subproject (one Settings, one root). includeBuild adds a separate, independent build as a composite build with its own settings file.

include puts rooms inside your house; includeFlat treats the house next door as a room of yours — same neighbourhood, different building.

saying these in an interview costs you the question

  • Claiming includeFlat creates a composite build / separate Settings.
  • Saying the project's logical path becomes nested rather than a direct child of root.
  • Thinking it resolves relative to rootDir instead of rootDir's parent.

context