CLI tool
jjohannes/idiomatic-gradle avatar
jjohannes/idiomatic-gradle

idiomatic-gradle shows what a large Gradle build is supposed to look like

How to idiomatically structure a large build with Gradle 8 or Gradle 9

559 stars23 forksJavaApache-2.0

At a glance

What is it?
idiomatic-gradle is a worked example of structuring a large Gradle 8 or Gradle 9 build, with three products composed of independent modules, aggregation subprojects that hold no code, and all build logic expressed as convention plugins. The project exists to be read, and the README is explicit about the practices it is trying to retire.
Who is it for?
Adopt idiomatic-gradle if you are restructuring a multi-project Gradle build and want a worked reference for module boundaries, aggregation projects and convention plugins. Skip it if your build is a handful of modules, since the convention plugin machinery is overhead at that size, and skip it if you are on Groovy DSL and not planning to move.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 30 days ago.
What is it written in?
Mainly Java, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Three products, each a Gradle build of its own

The example is a software composed of three products: a game engine, a graphics renderer that plugs into the engine, and a game called jamcatch built on the engine. The structure is a monorepo, with all three products living in one Git repository, and the README notes they could just as well be moved into separate repositories with the same build setup. To demonstrate that, the products are also published to a Maven repository, so each one can be built on its own by consuming the others from there, and you can explore that by opening an individual product folder in IntelliJ IDEA. The terminology is precise and worth copying. Each product is a Gradle build with its own settings.gradle.kts file, and each module is a Gradle subproject with its own build.gradle.kts file for dependency declaration. All modules are organised as independent, first-class entities under a shared product directory. To explore and build the example you clone the repository and open the root folder in IntelliJ IDEA, or you run the wrapper from the command line.

shell
./gradlew

The build file declares dependencies and nothing else

Each module folder contains the production code and the tests, which are plain Java independent of Gradle, plus a build.gradle.kts file that only contains a plugins and a dependencies block. The reason is stated: these files are used to clearly define the dependencies of that module on other modules of the same or other products, and on third-party open source modules. They are explicitly not used for further build configuration. That is the central discipline of the whole example, and it is what makes the modules independently buildable. The module layout inside a product looks like this.

shell
└── jamcatch
    └── jamcatch-actors
        β”œβ”€β”€ src
        β”‚   β”œβ”€β”€ main/java
        β”‚   β”œβ”€β”€ test/java
        β”‚   └── testEnd2end/java
        └── build.gradle.kts

Each module has unit tests using Gradle's default setup for Java projects with the src/test/java folder, and the modules of the jamcatch product also have end2end tests that run the module in the context of a complete application, which demonstrates the flexible testing and dependency management capabilities of Gradle for defining custom test sets with individual dependencies.

Aggregation projects compose, and hold no code

Applications and reports are composed from the modules by separate Gradle subprojects that the README calls aggregation subprojects, and these contain no code themselves. Their only build.gradle.kts file defines the dependencies to be aggregated.

shell
└── aggregation
    β”œβ”€β”€ app-jamcatch
    β”‚   └── build.gradle.kts
    β”œβ”€β”€ app-jamcatch-debug
    β”‚   └── build.gradle.kts
    └── reports
        └── build.gradle.kts

The result is that an application is a named list of module dependencies, and a debug variant is another one. The README gives the run command.

code
./gradlew :aggregation:app-jamcatch:run

You can then modify the dependencies in that build.gradle.kts and run again to explore the topic. This is the piece that makes the monorepo and the multirepo case genuinely equivalent: the composition point is the same file either way, and the only thing that changes is whether the dependencies resolve to sibling directories or to published artefacts in the Maven repository. The repository's top-level layout follows the same logic, with a `dependency-versions/` directory, an `aggregation/` directory, a `gradle-conventions/` directory and the three product directories sitting next to a root `settings.gradle.kts` and `build.gradle.kts`.

All build logic lives in convention plugins

The build logic section is the heart of the example. All individual Gradle build configurations are located in the gradle-conventions directory, under src/main/kotlin, and it holds standard configuration for Java compilation and testing along with more specific configurations for publishing and the end2end testing setup. To organise that configuration across several files, Gradle's Convention Plugins concept is used, so individual .gradle.kts files for different configuration aspects can be written and then treated as plugins, which allows reusing and composing those aspects in a flexible way. Treating every configuration as a plugin also allows publishing them to a Maven repository, which is how the convention plugins become shareable rather than local to one build. Technically gradle-conventions is itself a Gradle build, the same way the products are, except that this one produces Gradle plugins. The audience here is anyone whose build scripts have grown into a pile of cross-project configuration, and the claim the project makes is that a build where every relationship is expressed as a dependency is easier to read than one where relationships are encoded in the configuration of the containing project.

The list of practices the example is designed to retire

The caution block in the README is the most useful part of the documentation, because it names what the structure replaces. There are no direct dependencies between declared tasks, except for extending lifecycle tasks such as `assemble` or `check`. There are no direct dependencies between tasks from different subprojects. There is no cross-project configuration, in the form of subproject or allprojects blocks. And each build script of a subproject is simpler to read, because all relationships to other projects are expressed in terms of dependencies. There is also an explicit warning against a specific anti-pattern, which is putting common code into a parent folder rather than into a clearly defined common module. The documentation shows the shape of that mistake, with a src directory at the product level sitting alongside the real module directories, marked as something not to do. The rule it points to instead is that inside a product, shared code should live in a clearly defined common module, and the example gives the name, engine/javarca-model. This is the kind of convention that only pays off at scale, which is why the project is aimed at large builds.

Two other routes to the same dependency problem

The project is one of three that share the same example with a different focus, and the other two are worth knowing about because they solve the same problem differently. There is the gradle-project-setup-howto repository, which focuses on a full mono-repo setup, and javarcade, which focuses on structured dependency management with the JavaRCA recipe. The README also notes two alternatives to the Gradle approach itself. One is the language of the configuration: the project uses Gradle's Kotlin DSL for configuration files, but you can use any JVM language, including pure Java, to configure Gradle, with the gradle-plugins-howto repository covering alternative implementations of the plugin concept. The other is the notation for dependencies themselves. The example uses the build.gradle.kts files as dependency definition files, but the alternative is to use module-info.java files as part of the Java Module System, JPMS, to define dependencies directly in Java instead of using Gradle-specific Kotlin notation. That alternative is the subject of the javarca.de site, a Java Recipe for Carefree dependency Administration. The trade is real: JPMS keeps the dependency list in the place the compiler already reads, at the cost of tying your build to the module system.

What the example is and is not

It is worth being clear about what this repository is, because the name can mislead. The homepage is a video playlist, and the README points to an Understanding Gradle video series for the features used. This is a teaching and reference codebase rather than a library you would depend on, the licence is Apache 2.0, the primary language is Java, and the repository has no GitHub releases. The topics list android, examples, gradle and java, which is consistent with that reading. Two operational details are worth noting for anyone who clones it. The build uses a Gradle wrapper, so `./gradlew` is the entry point rather than a system Gradle, and the repository includes a `renovate.json` for dependency updates along with a `dependency-versions/` directory that presumably centralises version declarations. The last push was on 2026-09-23. The documentation available here does not set out a supported Gradle version matrix, so if you are on a specific Gradle 8 or 9 release, check the wrapper properties and the series yourself before assuming the example matches your toolchain.

Editorial conclusion

Adopt idiomatic-gradle if you are restructuring a multi-project Gradle build and want a worked reference for module boundaries, aggregation projects and convention plugins. Skip it if your build is a handful of modules, since the convention plugin machinery is overhead at that size, and skip it if you are on Groovy DSL and not planning to move. Before you start, note that the example defines dependencies in build.gradle.kts files, and that the alternative it points to is the Java Module System rather than Gradle notation at all.

Frequently asked questions

What is idiomatic-gradle and what does it demonstrate?

It is a worked example of how to use Gradle in a structured way, aimed at complex project setups rather than simple ones, with Gradle 8 or Gradle 9. The example software is a game engine, a graphics renderer that plugs into it, and a game called jamcatch built on the engine.

How does idiomatic-gradle structure modules and products?

Each product is a Gradle build with its own settings.gradle.kts, and each module is a Gradle subproject with its own build.gradle.kts used only for dependency declaration. Modules are independent, first-class entities under a shared product directory, and shared code belongs in a clearly defined common module rather than in a parent folder.

What are the aggregation subprojects in idiomatic-gradle?

They are separate Gradle subprojects that compose applications and reports from the modules and contain no code themselves. Their only build.gradle.kts defines the dependencies to be aggregated, and an application is run with `./gradlew :aggregation:app-jamcatch:run`.

How does idiomatic-gradle handle all the build configuration?

All individual Gradle build configurations live in the gradle-conventions directory, with standard configuration for Java compilation and testing plus more specific configurations for publishing and end2end testing. They are organised with Convention Plugins so each configuration aspect can be written as its own .gradle.kts file and treated as a plugin, which also allows publishing them to a Maven repository.

Official sources

  1. Issues
  2. jjohannes/idiomatic-gradle on GitHub
  3. License: Apache-2.0
  4. Project website
  5. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/jjohannes-idiomatic-gradle.svg)](https://hysenlabs.com/projects/jjohannes-idiomatic-gradle)

Was this useful?