Java Tooling
A comprehensive guide to Java development tooling. Configuration best practices for build systems, static analysis, CI quality gates, dependency management, and formatting.
Categories
Build Configuration [HIGH]
Configure Gradle and Maven builds for correct dependency scoping and reproducibility.
| Rule | Description |
|---|---|
| build-api-vs-implementation-scope | Treat api vs implementation as a compile-avoidance lever, not just encapsulation |
| build-gradle-version-catalogs | Centralize dependency versions in a Gradle version catalog |
| build-maven-dependency-management-bom | Import a BOM in dependencyManagement instead of pinning every version |
| build-reproducible-builds | Ban dynamic version ranges and non-deterministic archive output |
Static Analysis [HIGH]
Catch defects and boundary violations at build time with static analysis tools.
| Rule | Description |
|---|---|
| static-analysis-archunit-boundary-tests | Enforce package and layer boundaries with ArchUnit tests |
| static-analysis-error-prone-compiler-plugin | Run error prone as a javac plugin, not a separate analysis pass |
| static-analysis-nullaway | Enforce default non-null with NullAway, not just Objects.requireNonNull |
| static-analysis-spotbugs-ci-gate | Fail the build on SpotBugs findings, don't just report them |
CI Quality Gates [MEDIUM]
Gate CI on build cache, coverage, warnings, and mutation testing so it enforces real quality.
| Rule | Description |
|---|---|
| ci-quality-gates-build-cache | Enable a shared remote build cache so CI time tracks what changed |
| ci-quality-gates-coverage-threshold | Gate CI on a deliberately scoped coverage threshold, not a blunt global percentage |
| ci-quality-gates-fail-on-warnings | Treat compiler warnings as errors in CI |
| ci-quality-gates-mutation-testing | Use PITest to verify tests actually assert something, not just execute lines |
Dependency Management [MEDIUM]
Keep dependency versions and transitive exposure under single-source control.
| Rule | Description |
|---|---|
| dependency-management-lockfile-or-bom | Pick one source of truth — dependency lockfile or BOM, not neither |
| dependency-management-minimal-transitive-exposure | Exclude unneeded transitive dependencies instead of accepting the full graph |
| dependency-management-version-catalog-single-source | Make the version catalog the only place a version number can appear |
| dependency-management-vulnerability-scanning | Scan dependencies for known CVEs on every build, not at release time |
Lint & Formatting [MEDIUM]
Enforce consistent formatting and import order without relying on human review.
| Rule | Description |
|---|---|
| lint-format-checkstyle-config | Scope Checkstyle to structural checks, not formatting |
| lint-format-google-java-format | Adopt google-java-format for zero-config, deterministic formatting |
| lint-format-import-order | Enforce deterministic import order, ban wildcard imports |
| lint-format-spotless-apply-check | Gate CI on spotlessCheck, reserve spotlessApply for local hooks |
Quick Reference
Build Configuration
# gradle/libs.versions.toml
[versions]
jackson = "2.17.0"
[libraries]
jackson-databind = { module = "com.fasterxml.jackson.core:jackson-databind", version.ref = "jackson" }
[bundles]
testing = ["junit-jupiter", "assertj-core", "mockito-core"]
Static Analysis
@AnalyzeClasses(packages = "com.example")
class ArchitectureTest {
@ArchTest
static final ArchRule domainStaysFree = noClasses()
.that().resideInAPackage("..domain..")
.should().dependOnClassesThat().resideInAnyPackage("..infrastructure..", "org.springframework..");
}
CI Quality Gates
tasks.withType<JavaCompile>().configureEach {
options.compilerArgs.addAll(listOf("-Xlint:all", "-Werror"))
}
Dependency Management
# same PR that bumps the BOM version
./gradlew dependencies --write-locks
git add gradle.lockfile
Lint & Formatting
tasks.check {
dependsOn(tasks.spotlessCheck) // CI verifies, never rewrites
}
See Also
- java-coding-standards - General Java coding standards and best practices
- java-testing - Test-writing best practices for JUnit 5, AssertJ, Mockito, and Testcontainers