Maven compile usually becomes Gradle implementation, or api for a library. Runtime becomes runtimeOnly, test becomes testImplementation and testRuntimeOnly, provided becomes compileOnly, and import maps to a platform. The mapping decides what ships to production, so a wrong choice is a security issue as well as a build issue.
The mapping table
| Maven scope | Gradle configuration | Notes |
|---|---|---|
| compile | implementation, or api for libraries | api exposes the dependency to consumers |
| runtime | runtimeOnly | Not on the compile classpath |
| test | testImplementation, testRuntimeOnly | Split by compile versus run |
| provided | compileOnly | War plugin adds providedCompile and providedRuntime |
| import | platform() or enforcedPlatform() | Used for BOMs in dependencyManagement |
This follows the Gradle migration guide. It explains that Gradle distinguishes dependencies required to build a module from those required to build a module that depends on it, while Maven makes no such distinction, so published POMs often include dependencies that consumers do not need.
Why scopes are a security topic
Scopes control what ends up in the packaged application. A library marked test in Maven should never ship, and a scanner that reports it as a production finding wastes time. A library that was provided by the application server in Maven but lands in implementation in Gradle may now be packaged into your archive, adding code and its advisories to the runtime you deploy.
The Gradle guide specifically notes that the War plugin's providedCompile and providedRuntime keep dependencies out of the WAR file while including them on runtime and test runtime classpaths. Choose the one that matches the old behavior.
Steps
- List every dependency with its Maven scope.
- Convert using the table.
- Build the production artifact, then list what is inside it.
- Compare that list with the one from the Maven artifact.
- Anything new in the Gradle artifact needs a reason.
Common mistakes
- Using api everywhere. It is only available with the Java Library plugin and leaks dependencies to consumers. The guide says to use implementation in most cases, particularly for applications.
- Treating provided as test. They are different: provided is needed at compile time but supplied by the runtime.
- Forgetting that a POM only dependency is treated differently. Depending on a POM only publication adds its dependencies as normal transitive dependencies.
For the scanner side, see Maven dependency scope and scan findings and Gradle api versus implementation security.
Try it yourself
Click through it at your own pace, or open it full screen. Look at how knowing whether a vulnerable package is actually used reduces the findings you must act on, which is the same question scopes answer at build time.
Safeguard's reachability analysis and dependency scanning help sort findings by whether the code can be reached. A free scan is at gold.safeguard.sh/scan.
Quick check
Is implementation the same as compile?
It is the usual replacement, but api is the closer match when a library must expose a dependency to its consumers.
Where do test scoped libraries go?
testImplementation for compiling tests and testRuntimeOnly for running them. They should not be in the shipped artifact.
Sources
Last reviewed: 19 October 2026.