Convention Plugins Migration Strategy
Tracking issue: #12258
Updated: 2026-10-02. The instrumentation extraction described below is staged
locally and has not been committed or published as a PR.
Replace Groovy script plugins and parent-driven module configuration with Kotlin
convention plugins. Keep module entry points stable while moving one cohesive
responsibility at a time, preserving artifacts, task behavior, and incremental
builds.
Current state
-
Module entry points already exist under dd-trace-java.module.* in buildSrc.
The namespace is singular: module, not the earlier proposal's modules.
-
dd-trace-java.conventions.java composes dependency locking and the remaining
gradle/java_deps.gradle and gradle/java_no_deps.gradle scripts.
PR #12331 migrated the
remaining direct consumers, and PR #12333
moved composition into the convention and deleted gradle/java.gradle.
-
build-logic/conventions already contains the Kotlin, Scala, and SLF4J-simple
testing conventions, plus dd-trace-java.mass.
-
build-logic also contains the smoke-test application and Testcontainers plugins.
These are separate from the dd-trace-java.module.smoke-test module convention.
-
Fifteen Groovy scripts remain directly under gradle/:
codenarc.gradle ddprof-override.gradle dependencies.gradle
forbiddenapis.gradle jacoco.gradle java_deps.gradle
java_no_deps.gradle maven-pom.gradle publish.gradle
repositories.gradle spotless.gradle spotbugs.gradle
test-suites.gradle tries.gradle util.gradle
Instrumentation configuration: staged extraction
dd-trace-java.module.instrumentation now owns the module defaults previously
configured by the subprojects {} block in
dd-java-agent/instrumentation/build.gradle:
- Apply
dd-trace-java.build-time-instrumentation and dd-trace-java.muzzle.
- Register the advice-scanning,
newTaskFor, and request-context rewrite plugins,
with agent-tooling's buildTimeInstrumentationToolingPlugins artifact on
buildTimeInstrumentationPlugin.
- Exclude the vendored SnakeYAML engine from
muzzleBootstrap.
- Disable instrumentation Javadoc and configure the main and instrumentation
forbidden-API signature files.
- Supply common annotation processors, compile-only, implementation, and test
dependencies, including interoperability with core JDK instrumentations.
- Supply the base dependencies of additional
main_javaNImplementation
configurations, including configurations registered after plugin application.
- Set
-Dtest.dd.latestDepTest=true on latestDepTest, latestDepForkedTest,
and latestDepTestForkedTest.
The parent still owns dependency aggregation, the instrumentation shadow jar,
index generation, and muzzle-report aggregation. Its small subprojects block
still adds instrumentation projects to the parent, excluding the Redis and Tibco
stub projects. Removing that aggregation is a separate change.
Kotlin DSL and plugin availability
The staged change declares java in the shared Java convention's plugins {}
block so consuming conventions can use generated Java dependency accessors.
Forbidden APIs is available to convention compilation through
implementation(libs.forbiddenapis) in buildSrc/build.gradle.kts. The
instrumentation convention declares id("de.thetaphi.forbiddenapis") and uses
the generated forbiddenApis {} accessor. The redundant root plugin declaration
is removed; other Java modules retain their existing application through
gradle/forbiddenapis.gradle.
The two binary plugins produced by the same buildSrc build still use:
pluginManager.apply("dd-trace-java.build-time-instrumentation")
pluginManager.apply("dd-trace-java.muzzle")
Putting these IDs in this convention's plugins {} block currently fails during
generatePrecompiledScriptPluginAccessors: their implementations are not yet
available there. Keep the runtime application until the implementations are
available on the convention compilation classpath. Moving the implementations
and their composing conventions together to build-logic is a later step.
Version catalogs use versionCatalogs.named("libs"). A lookup such as
findLibrary("autoservice-processor").get() unwraps an Optional and retains the
dependency Provider; it does not eagerly resolve the dependency. Reuse repeated
lookups without introducing generated-catalog classpath workarounds.
Local validation
- The agent and instrumentation shadow jars are byte-identical to the baseline
built from the same revision, a959c192be.
- Warm agent builds preserve the baseline task outcomes.
- Focused instrumentation tests, main and additional-source-set forbidden-API
checks, and representative SpotBugs tasks passed during the extraction.
- Moving
java into the shared convention passed forced accessor regeneration
and Kotlin compilation, followed by the agent build and jar comparison.
These checks used an isolated Maven-local directory for the agent build because
the normal local repository lacks sisu-guice-3.1.0-no_aop.jar. The token-gated
Akka HTTP 10.6 module was omitted consistently from both compared builds.
The separate IntelliJ/JUnit launcher issue is an environment-dependent follow-up;
its workaround is excluded from this migration.
Ownership and naming
Keep dd-trace-java.module.<role> for module entry points and
dd-trace-java.conventions.<capability> for reusable behavior. Test-only leaves
belong under dd-trace-java.conventions.testing.*. Preserve established feature
plugin IDs such as dd-trace-java.muzzle and dd-trace-java.test-jvm-constraints.
Prefer precompiled Kotlin scripts for composition. Use binary plugins and typed
tasks or extensions where the behavior needs an implementation or a consumer API.
Do not introduce an umbrella extension, feature flag, or helper layer just to
relocate existing configuration.
Keep module defaults in the convention that owns the module role. Aggregation,
packaging, and distribution wiring stay with the project or plugin that produces
the aggregate artifact. In particular, the agent distribution should eventually
compose only the Java, archive, and publishing behavior it needs.
Use build-logic/conventions for independently applied conventions and for
conventions composed within build-logic. Keep conventions needed by existing
buildSrc plugins in buildSrc until their composing plugins can move too.
Project plugin resolution through an included build does not make its classes
available to the parent buildSrc classloader.
Remaining migration sequence
- Publish the instrumentation extraction as a focused PR. Preserve the parent
aggregation and the existing module plugin IDs.
- Apply the same ownership split to smoke tests: move module Javadoc policy,
forbidden-API policy, and agent-jar test wiring out of dd-smoke-tests parent
configuration. Preserve its shared dependency aggregation and verify Play,
native-image, and custom application-jar cases separately.
- Extract independent leaves from the Java scripts. Split Groovy/Spock support
from language-neutral test dependencies; migrate CodeNarc with Groovy support.
Keep Kotlin and Scala as opt-in siblings that react to Groovy when present.
- Extract default Java compilation, compiler/toolchain helpers, and additional
versioned source sets as separate changes. Keep temporary delegating Groovy
API shims while existing consumers still need them.
- Extract archive, Javadoc, JaCoCo, forbidden-API, SpotBugs, Spotless, and testing
behavior into focused conventions. Preserve JVM test suites, special test
targets, test jars, task laziness, and toolchain behavior.
- Migrate publication/POM handling and repository/shared-dependency policy in
separate changes. Repositories must be verified across the independent main,
buildSrc, build-logic, and nested smoke-test builds.
- Move the remaining composing conventions and binary plugin implementations
from buildSrc to build-logic together. Replace imperative plugin application
with plugins {} where the new classpath supports it.
- Delete each obsolete script or compatibility shim once no consumer remains.
Moving configuration alone does not establish configuration-cache support.
The detailed Java decomposition uses focused conventions for
java-compilation, java-toolchains, java-versioned-source-sets, archives,
javadoc, code-quality, jacoco, and testing. Testing further separates
dependencies, execution, JVM suites, test jars, Testcontainers, and opt-in
language support. Introduce each when its responsibility is extracted.
Verification for each slice
- Compile the affected convention plugins and configure representative consumers.
- Exercise the affected behavior, including late-registered tasks/source sets and
both ordinary and latest-dependency test variants where applicable.
- For packaging-affecting changes, compare artifacts from the same revision and
environment. Compare archive contents if an intentional version change prevents
byte equality.
- Run the same Gradle command again and compare task outcomes. Investigate an
unexpected rerun instead of treating a successful build as sufficient.
- Run the applicable formatting checks and
git diff --check.
- Verify IntelliJ sync when plugin classpaths, Kotlin accessors, or dependency
models change. Record local repository or credential exclusions explicitly.
- Claim configuration-cache compatibility only after the relevant invocation
has been tested with configuration caching enabled.
Related work
Existing issue references are retained here:
Convention Plugins Migration Strategy
Tracking issue: #12258
Updated: 2026-10-02. The instrumentation extraction described below is staged
locally and has not been committed or published as a PR.
Replace Groovy script plugins and parent-driven module configuration with Kotlin
convention plugins. Keep module entry points stable while moving one cohesive
responsibility at a time, preserving artifacts, task behavior, and incremental
builds.
Current state
Module entry points already exist under
dd-trace-java.module.*inbuildSrc.The namespace is singular:
module, not the earlier proposal'smodules.dd-trace-java.conventions.javacomposes dependency locking and the remaininggradle/java_deps.gradleandgradle/java_no_deps.gradlescripts.PR #12331 migrated the
remaining direct consumers, and PR #12333
moved composition into the convention and deleted
gradle/java.gradle.build-logic/conventionsalready contains the Kotlin, Scala, and SLF4J-simpletesting conventions, plus
dd-trace-java.mass.build-logicalso contains the smoke-test application and Testcontainers plugins.These are separate from the
dd-trace-java.module.smoke-testmodule convention.Fifteen Groovy scripts remain directly under
gradle/:Instrumentation configuration: staged extraction
dd-trace-java.module.instrumentationnow owns the module defaults previouslyconfigured by the
subprojects {}block indd-java-agent/instrumentation/build.gradle:dd-trace-java.build-time-instrumentationanddd-trace-java.muzzle.newTaskFor, and request-context rewrite plugins,with
agent-tooling'sbuildTimeInstrumentationToolingPluginsartifact onbuildTimeInstrumentationPlugin.muzzleBootstrap.forbidden-API signature files.
dependencies, including interoperability with core JDK instrumentations.
main_javaNImplementationconfigurations, including configurations registered after plugin application.
-Dtest.dd.latestDepTest=trueonlatestDepTest,latestDepForkedTest,and
latestDepTestForkedTest.The parent still owns dependency aggregation, the instrumentation shadow jar,
index generation, and muzzle-report aggregation. Its small
subprojectsblockstill adds instrumentation projects to the parent, excluding the Redis and Tibco
stub projects. Removing that aggregation is a separate change.
Kotlin DSL and plugin availability
The staged change declares
javain the shared Java convention'splugins {}block so consuming conventions can use generated Java dependency accessors.
Forbidden APIs is available to convention compilation through
implementation(libs.forbiddenapis)inbuildSrc/build.gradle.kts. Theinstrumentation convention declares
id("de.thetaphi.forbiddenapis")and usesthe generated
forbiddenApis {}accessor. The redundant root plugin declarationis removed; other Java modules retain their existing application through
gradle/forbiddenapis.gradle.The two binary plugins produced by the same
buildSrcbuild still use:Putting these IDs in this convention's
plugins {}block currently fails duringgeneratePrecompiledScriptPluginAccessors: their implementations are not yetavailable there. Keep the runtime application until the implementations are
available on the convention compilation classpath. Moving the implementations
and their composing conventions together to
build-logicis a later step.Version catalogs use
versionCatalogs.named("libs"). A lookup such asfindLibrary("autoservice-processor").get()unwraps anOptionaland retains thedependency
Provider; it does not eagerly resolve the dependency. Reuse repeatedlookups without introducing generated-catalog classpath workarounds.
Local validation
built from the same revision,
a959c192be.checks, and representative SpotBugs tasks passed during the extraction.
javainto the shared convention passed forced accessor regenerationand Kotlin compilation, followed by the agent build and jar comparison.
These checks used an isolated Maven-local directory for the agent build because
the normal local repository lacks
sisu-guice-3.1.0-no_aop.jar. The token-gatedAkka HTTP 10.6 module was omitted consistently from both compared builds.
The separate IntelliJ/JUnit launcher issue is an environment-dependent follow-up;
its workaround is excluded from this migration.
Ownership and naming
Keep
dd-trace-java.module.<role>for module entry points anddd-trace-java.conventions.<capability>for reusable behavior. Test-only leavesbelong under
dd-trace-java.conventions.testing.*. Preserve established featureplugin IDs such as
dd-trace-java.muzzleanddd-trace-java.test-jvm-constraints.Prefer precompiled Kotlin scripts for composition. Use binary plugins and typed
tasks or extensions where the behavior needs an implementation or a consumer API.
Do not introduce an umbrella extension, feature flag, or helper layer just to
relocate existing configuration.
Keep module defaults in the convention that owns the module role. Aggregation,
packaging, and distribution wiring stay with the project or plugin that produces
the aggregate artifact. In particular, the agent distribution should eventually
compose only the Java, archive, and publishing behavior it needs.
Use
build-logic/conventionsfor independently applied conventions and forconventions composed within
build-logic. Keep conventions needed by existingbuildSrcplugins inbuildSrcuntil their composing plugins can move too.Project plugin resolution through an included build does not make its classes
available to the parent
buildSrcclassloader.Remaining migration sequence
aggregation and the existing module plugin IDs.
forbidden-API policy, and agent-jar test wiring out of
dd-smoke-testsparentconfiguration. Preserve its shared dependency aggregation and verify Play,
native-image, and custom application-jar cases separately.
from language-neutral test dependencies; migrate CodeNarc with Groovy support.
Keep Kotlin and Scala as opt-in siblings that react to Groovy when present.
versioned source sets as separate changes. Keep temporary delegating Groovy
API shims while existing consumers still need them.
behavior into focused conventions. Preserve JVM test suites, special test
targets, test jars, task laziness, and toolchain behavior.
separate changes. Repositories must be verified across the independent main,
buildSrc,build-logic, and nested smoke-test builds.from
buildSrctobuild-logictogether. Replace imperative plugin applicationwith
plugins {}where the new classpath supports it.Moving configuration alone does not establish configuration-cache support.
The detailed Java decomposition uses focused conventions for
java-compilation,java-toolchains,java-versioned-source-sets,archives,javadoc,code-quality,jacoco, andtesting. Testing further separatesdependencies, execution, JVM suites, test jars, Testcontainers, and opt-in
language support. Introduce each when its responsibility is extracted.
Verification for each slice
both ordinary and latest-dependency test variants where applicable.
environment. Compare archive contents if an intentional version change prevents
byte equality.
unexpected rerun instead of treating a successful build as sufficient.
git diff --check.models change. Record local repository or credential exclusions explicitly.
has been tested with configuration caching enabled.
Related work
Existing issue references are retained here: