How to Ban Forbidden Types and Imports in Scala and SBT
When upgrading legacy Scala applications from the older javax.* namespace to the modern jakarta.* namespace, a common friction point arises: developers accidentally importing legacy types like javax.inject.Inject instead of jakarta.inject.Inject. Because many frameworks support both annotations for backward compatibility, automated unit tests rarely catch this mistake.
To maintain code quality and architectural consistency, you need a way to enforce this rule during compilation or CI pipelines. Here are the best, production-ready solutions to ban forbidden package imports in an SBT-based Scala project.
Method 1: Enforce Import Rules using Scalafix (Recommended)
The cleanest and most robust way to forbid specific types or packages across non-generated source code is by using Scalafix along with its built-in Disable or DisableSyntax rules.
Step 1: Add the Scalafix Plugin
In your project/plugins.sbt file, add the Scalafix SBT plugin:
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.11.1")Step 2: Enable SemanticDB in build.sbt
Scalafix uses SemanticDB to inspect full type information reliably across your codebase:
thisBuild / semanticdbEnabled := true
thisBuild / semanticdbVersion := scalafixSemanticdb.revisionStep 3: Configure Banned Packages in .scalafix.conf
Create a .scalafix.conf file in the root directory of your project and configure the Disable rule to flag symbols matching the target package:
rules = [
Disable
]
Disable.packages = [
{
package = "javax.inject"
message = "Use jakarta.inject instead of javax.inject for dependency injection."
}
]Step 4: Run Scalafix in CI
You can run Scalafix in check mode to break the build whenever forbidden imports are detected:
sbt "scalafixAll --check"Method 2: Use Wartremover for Compile-Time Linting
If your project already uses Wartremover for static analysis, you can leverage custom warnings or custom warts to block specific package usages at compile time.
Step 1: Add Wartremover to project/plugins.sbt
addSbtPlugin("org.wartremover" % "sbt-wartremover" % "3.1.5")Step 2: Configure Banned Namespaces in build.sbt
Use Wartremover's Wart.Custom capabilities or configure symbol rules depending on the version supported in your build setup.
Method 3: Exclude the Legacy Dependency on Compile Classpath
If your code shouldn't depend on javax.inject at compile time at all, and it's only pulled in as a transitive dependency by third-party libraries, you can exclude the JAR during compilation.
Add the following exclusion rule to your build.sbt:
libraryDependencies := libraryDependencies.value.map {
_.exclude("javax.inject", "javax.inject")
}Note: If runtime frameworks require the legacy library for runtime reflection, combine this approach with Runtime scope dependencies to ensure it remains available when running the application.
Summary
Using Scalafix with the Disable.packages rule is the recommended solution. It gives clear error messages to developers, integrates seamlessly with CI/CD tools, and ensures non-generated Scala source code strictly adheres to modern jakarta.inject standards.