# How to Convert a Single-Project Build into a Multi-Project Build in Gradle

version 9.0.0

When your Gradle project grows, you may want to split it into multiple subprojects to improve modularity, maintainability, and parallel execution.

This guide explains how to convert a single-project build into a multi-project build.

## Why Use a Multi-Project Build?

A single-project build manages all source code and dependencies in one `build.gradle(.kts)` file.

In contrast, a multi-project build consists of:

- A root project that contains shared configurations and dependencies, as defined in `settings.gradle(.kts)`.

- Multiple subprojects, each with its own `build.gradle(.kts)`, dependencies, and tasks.

## Step 1: Project Structure

Let’s start with a typical single-project setup, created by `gradle init`:

    root
    ├── settings.gradle(.kts) (1)
    └── app (2)
        ├── build.gradle(.kts)
        └── src/

1 | Defines the root project settings
2 | The single application module

Alternatively, your single project may look like this:

    app (root) (1)
    ├── build.gradle(.kts)
    ├── settings.gradle(.kts)
    └── src/

1 | The project root, also acting as the application module

| If your project looks like this, nest the `app` folder inside a new `root` folder before your get started.

After converting it into a multi-project build, the structure will look like this:

    root
    ├── settings.gradle(.kts) (1)
    ├── app (2)
    │   ├── build.gradle(.kts)
    │   └── src/
    └── library (3)
        ├── build.gradle(.kts)
        └── src/

1 | Defines the subprojects
2 | The original application module, now a subproject
3 | A new library module

## Step 2: Create One or More New Subprojects

Now you can create your additional projects as directories in the `root`. They will all become subprojects of the root.

To continue our example, we create a new subproject called `library`:

    cd root
    mkdir library

Ensure that the original project (`app`) remains at the same level as the new `library` subproject:

    root
    ├── app (1)
    │   └── some files
    └── library (2)

1 | The original application module
2 | The newly created subproject

Move source files into their appropriate folders.

In our example, the reusable code is moved to the `library` subproject:

    root
    ├── app (1)
    │   └── src/
    └── library (2)
        └── src/

1 | The application module, containing its own source code
2 | The new library module, storing reusable code

## Step 3: Update `settings.gradle(.kts)`

Ensure that `settings.gradle(.kts)` is located in the root directory:

    root
    ├── settings.gradle(.kts) (1)
    ├── app (2)
    │   └── src/
    └── library (3)
        └── src/

1 | The single settings file for the entire build
2 | The application module
3 | The new library module

Modify `settings.gradle(.kts)` to include the new subprojects.

In our example, we add `library` to our `include`:

settings.gradle.kts

    rootProject.name = "root"
    include("app", "library")

settings.gradle

    rootProject.name = "root"
    include("app", "library")

There should be no other settings files.

## Step 4: Create `build.gradle(.kts)` Files for Each Subproject

Each subproject must have its own `build.gradle(.kts)` file:

    root
    ├── settings.gradle(.kts) (1)
    ├── app (2)
    │   ├── build.gradle(.kts) (3)
    │   └── src/
    └── library (4)
        ├── build.gradle(.kts) (5)
        └── src/

1 | Defines the subprojects
2 | The application subproject
3 | Defines the `app` build
4 | The library subproject
5 | Defines the `library` build

The `app` build file should look very similar to the original build file. It holds all the dependencies and configurations needed to build the `app` subproject.

Now, it also has a dependency on the `library` subproject:

app/build.gradle.kts

    plugins {
        application
        id("org.jetbrains.kotlin.plugin.serialization") // Now the plugin is applied
    }

    repositories {
        mavenCentral()
    }

    dependencies {
        implementation("com.google.guava:guava:33.3.1-jre")
        implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")
        implementation(project(":library")) // Uses the library module
    }

    java {
        toolchain {
            languageVersion = JavaLanguageVersion.of(21)
        }
    }

    application {
        mainClass = "org.example.App"
    }

app/build.gradle

    plugins {
        id('application')
        id("org.jetbrains.kotlin.plugin.serialization") // Now the plugin is applied
    }

    repositories {
        mavenCentral()
    }

    dependencies {
        implementation("com.google.guava:guava:33.3.1-jre")
        implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")
        implementation(project(":library")) // Uses the library module
    }

    java {
        toolchain {
            languageVersion = JavaLanguageVersion.of(21)
        }
    }

    application {
        mainClass = "org.example.App"
    }

The `library` build file should be new. It holds all the dependencies and configurations needed to build the `library` subproject:

library/build.gradle.kts

    plugins {
        id("java-library")
        id("org.jetbrains.kotlin.plugin.serialization") // Now the plugin is applied
    }

    repositories {
        mavenCentral()
    }

    dependencies {
        implementation("org.apache.commons:commons-lang3:3.17.0")
        implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")
    }

library/build.gradle

    plugins {
        id("java-library")
        id("org.jetbrains.kotlin.plugin.serialization") // Now the plugin is applied
    }

    repositories {
        mavenCentral()
    }

    dependencies {
        implementation("org.apache.commons:commons-lang3:3.17.0")
        implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")
    }

## Step 5: Modify or Delete the Root `build.gradle(.kts)`

A build file in the root project is optional:

    root
    ├── settings.gradle(.kts) (1)
    ├── build.gradle(.kts) (2)
    ├── app (3)
    │   ├── build.gradle(.kts)
    │   └── src/
    └── library (4)
        ├── build.gradle(.kts)
        └── src/

1 | Defines the subprojects
2 | Optional (for shared configurations)
3 | The application module
4 | The new library module

If you need to configure shared repositories and settings, you can use the root `build.gradle(.kts)`:

build.gradle.kts

    plugins {
        id("org.jetbrains.kotlin.plugin.serialization") version "1.9.21" apply false
    }

    subprojects {
        repositories {
            mavenCentral()
        }
    }

build.gradle

    plugins {
        id 'org.jetbrains.kotlin.plugin.serialization' version '1.9.21' apply false
    }

    subprojects {
        repositories {
            mavenCentral()
        }
    }

You can take a look back at how the common 3rd party plugin called `org.jetbrains.kotlin.plugin.serialization` is applied in each build file.

However, shared logic should be handled using convention plugins rather than the root build file.

## Summary

This process can be repeated indefinitely to add more subprojects:

    root
    ├── settings.gradle(.kts) (1)
    ├── app (2)
    │   ├── build.gradle(.kts)
    │   └── src/
    ├── library-a (3)
    │   ├── build.gradle(.kts)
    │   └── src/
    ├── library-b (4)
    │   ├── build.gradle(.kts)
    │   └── src/
    └── library-c (5)
        ├── build.gradle(.kts)
        └── src/

1 | Defines the subprojects
2 | The application module
3 | A shared library module
4 | Another library module
5 | Yet Another library module

By following these steps, you can modularize your project, improve maintainability, and enable parallel execution.

If you wanted one of your libraries to be its own build, you should consider using Composite Builds (i.e., included builds) instead.