Why did my whole build rerun after a small change?
You changed one small thing, and the whole build ran again. FosterStack Cache keeps each module's result so your build can reuse it. In our runs Gradle reused most results, and Maven rebuilt every module above the one we edited.
The short answer
Picture three modules in a chain: core at the bottom, service on top of it, and app on top of that.
- Gradle: in our runs, an edit inside a method or a comment recompiled only the module you edited. Adding a public method also recompiled the module directly above it.
- Maven: in our runs, each of our three changes to the bottom module rebuilt all three modules, even a comment.
- Both: a change in the top module recompiled (Gradle) or rebuilt (Maven) only the top module. The two below it came from the cache.
- A dependency bump: only the module that uses the library recompiled (Gradle) or rebuilt (Maven). Going back to the old version restored both modules.
What to do about it
Our advice, not a test result:
- In Maven, batch edits to widely shared modules. Expect an edit there to rebuild everything above it.
- In Gradle, keep a shared module's public API steady. In our runs, an edit inside a method recompiled only that module.
- Bump a dependency in one change. Let one build fill the cache. Later builds restore it.
- Keep Gradle, Maven and Java versions the same on laptops and CI, so entries can be shared.
If a module you did not touch still rebuilds, see why the Gradle hit rate is low or why the Maven cache is not restoring. For Maven multi-module setup, see Maven Build Cache Extension in a Multi-Module Project.
Why Maven rebuilt more than Gradle
The Maven log shows that while it works out the cache key for service, it also works out the key for core. In the comment run, core had a new key and so did service (efd19a4c19b933af before, 3d90eee1e1f99534 after), though no file in service changed. That is consistent with a module's key including the keys of the modules it depends on.
Gradle's documentation describes the cache key for compiling a module as looking only at the public API of the modules it depends on. Our results are consistent with that. We checked results, not Gradle's internals.
How we checked
A chain of three modules: core, then service (uses core), then app (uses service). For each change, build 1 was the unchanged project and build 2 had the one change. This table shows build 2.
| The one change | Gradle: core / service / app | Maven: core / service / app |
|---|---|---|
A number in a method body in core | recompiled / from cache / from cache | built / built / built |
One comment in core | recompiled / from cache / from cache | built / built / built |
A new public method in core | recompiled / recompiled / from cache | built / built / built |
A number in app | from cache / from cache / recompiled | restored / restored / built |
For the dependency bump we used two modules: lib with no dependencies and app using Gson. Five builds ran on one server: Gson 2.10.1, 2.10.1 again, 2.11.0, 2.11.0 again, then back to 2.10.1. After the bump, lib came from the cache and app was recompiled (Gradle) or rebuilt (Maven). The builds after that restored both modules. The result was the same in Gradle and in Maven.
Every scenario started on a new, empty cache server, with a fresh project copy for every build. Downloads (the Gradle home for the Gson runs, the Maven repository) were shared across builds and held no cache entries. Gradle 9.8.0 ran only the compile tasks, with its local cache off. Maven 3.9.9 with Build Cache Extension 1.2.3 ran mvn verify with all eight default plugin versions pinned and compiled with Java release 17. Both used Temurin 21.0.12.1 and FosterStack Cache 0.2.1 on one Mac (arm64). Not tested: Gradle api dependencies, test and jar tasks in Gradle, annotation processors, Kotlin, a change to a public constant, a change in the middle module, a library used by both modules, other versions, bigger projects, Linux, build times.