Java › Build Tools: Maven & Gradle

What is the difference between Maven's <dependencies> scope values (compile, provided, runtime, test) and how do they affect what's included in the final packaged artifact?

Intermediate

Answer

compile (the default) is available at both compile-time and runtime, and IS included in the final packaged artifact and propagated transitively; provided is available at compile-time but expected to be supplied by the runtime environment (like servlet-api when deploying to an app server that already provides it), so it's excluded from the final package; runtime is needed only at runtime, not compile-time (like a JDBC driver implementation used only through a generic interface), and IS included in the package; test is available only during test compilation/execution and never included in the final package at all.
<dependencies>
    <dependency>
        <groupId>jakarta.servlet</groupId>
        <artifactId>jakarta.servlet-api</artifactId>
        <version>6.0.0</version>
        <scope>provided</scope>  <!-- servlet container already provides this at runtime -->
    </dependency>
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter</artifactId>
        <version>5.10.0</version>
        <scope>test</scope>  <!-- only needed for running tests, excluded from the deployable WAR -->
    </dependency>
</dependencies>
Real-world example A WAR file deployed to an existing Tomcat server correctly excludes the servlet-api JAR (marked provided scope) from its packaged output, avoiding a classpath conflict with the version Tomcat itself already supplies, while still allowing the code to compile against those servlet APIs during the build.

Common follow-ups: What happens if you incorrectly mark a scope as provided when the runtime environment actually doesn't supply that dependency?;How does Gradle's equivalent configuration (implementation, compileOnly, runtimeOnly, testImplementation) map to Maven's scopes?