Introducing Composite Builds

It’s not every day that we get to announce a feature that revolutionizes several software engineering workflows, but today is that day. Composite builds, a new feature in Gradle 3.1, enables an entirely new dimension in project organization.

Composite builds are a way to join multiple independent Gradle builds and build them together. The brevity of that statement does not fully convey all of the new possibilities, so let me show you how this will make your life as a developer a lot easier.

Joining projects

Many organizations split their code base into several independent projects, each having a dedicated repository and release cycle. Integration between the projects is managed using binary dependencies, e.g. JAR files published to a binary repository like Artifactory. This approach has many advantages, but can be inefficient when trying to rapidly develop and test changes that affect two or more of these projects at once.

Imagine for a moment that you are fixing a bug in a Java library that your application depends on. Your workflow probably looks something like the following:

  1. Change the library
  2. Publish the library to a local repository
  3. Add the local repository to your application’s repositories
  4. Change your application’s dependency to the new library version
  5. Test your application
  6. Repeat until the problem is fixed or you lose your mind

With composite builds, you can short-circuit this workflow by including the library’s build into your application’s build. Gradle will then automatically replace the binary dependency on the library with a project dependency—meaning that changes you make to the library become available to the application instantaneously:

The same approach works for plugins that your project depends on. You can now include a locally checked-out version of a plugin into your project’s build and get into the same kind of tight development loop between them:

The new includeBuild() API in settings.gradle even lets you write a Gradle build that dynamically includes other builds if they are available on the local file system. You could then import this composite into your IDE and do cross-repository refactoring or debugging.

Splitting Monoliths

Organizations that want to avoid the integration pains of multiple repositories tend to use a “monorepo”—a repository containing all projects, often including their dependencies and necessary tools. The upside is that all code is in one place and downstream breakages become visible immediately. But this convenience can come at the cost of productivity: a given developer will usually work only on a small part of a monorepo, but will still be forced to build all upstream projects, and that can mean a lot of waiting and wasted time. Likewise, importing large monorepo projects into an IDE often results in an unresponsive and overwhelming experience.

With composite builds, you can break your monorepo up into several independent builds within the same repository. Developers can work with the individual builds to get fast turnarounds or work with the whole composite when they want to ensure that everything still plays well together:

If you’re planning to move from a monolithic application to multiple independent ones, composite builds now offer a seamless migration strategy.

This is just the beginning

We plan to add a number of improvements to composite builds in upcoming releases:

  • Targeting tasks in an included build from the command line
  • Richer dependency substitution API with support for custom publications
  • Executing included builds in parallel
  • Integration with Gradle’s continuous build capabilities
  • Out of the box support for composite builds in IntelliJ and Eclipse

Of course, nothing is more important than feedback from real world usage. So please give composite builds a try in your own projects or have a look at the samples. Let us know about any problems, suggestions and cool things you built with it on the Gradle Forum.