Quick experimentation vs -Xfatal-warnings flag in Scala/sbt

Problem
I’ve always had mixed feelings about scalacOptions += "-Xfatal-warnings"
. On one hand, it’s an inevitable PR review and code quality boost—I no longer have to comment on unused variables or imports. On the other hand, it kills experimentation and creativity. I’ve spent countless hours fixing unused variables and imports just to test something quickly.
This issue only arises when -Xfatal-warnings
is enabled in build.sbt
.
Problematic code: Unused variables cause compilation error
[build.sbt]
ThisBuild / scalaVersion := "3.3.1"
lazy val root = project
.in(file("."))
.settings(
name := "minimal-project",
scalacOptions ++= Seq(
"-deprecation", // show deprecation warnings
"-unchecked", // additional warnings
"-Xfatal-warnings" // treat warnings as errors
)
)
[article21.scala]
package blog.article21
@main def demoProblem(): Unit =
val a = 2 // boom
val b = 3
val unused: String = "hello" // boom
println(b)
This results in compilation errors like:
We can’t even run our little experiment without resolving all warnings first. Frustrating!
Solution
We can modify the global: ~/.sbt/1.0/global.sbt
file to disable -Xfatal-warnings
when running inside the IDE, while keeping it enabled in the command line.
Before We Dive into the Solution
Here are some approaches I tried before finding the one I now use.
Approach 1 (bad idea)
Just drop -Xfatal-warnings
completely. Sure, it’s faster for prototyping—good for startups maybe? But it’s an oversimplification and a missed opportunity. Scala’s strong typing and compiler warnings can help you catch issues early and reduce PR nitpicks and conflicts.
Also, I’ve seen projects without this flag—it’s always the same story: tons of warnings and no way to know if your change added new ones.
Did I imagine it, or did some teams in the ’90s track warnings per project in an external backlog? 😅
Approach 2 (meh)
Add conditional logic to each build.sbt, using an environment variable to toggle the flag.
It worked, but I had to modify every project and invent a variable like SCALA_DISABLE_FATAL. Not a huge deal—unless you’re a guest contributor who doesn’t want to touch the build on your first PR.
Approach 3 (worse)
Live with it. “It’s not a big deal to remove an unused import.” Actually—it is. When you’re in a creative flow, you don’t want to be interrupted by compiler nags or have to constantly delete and re-add code just to run println.
A Better Solution
Wouldn’t it be great if:
- In the IDE, you’re free to experiment.
- In the terminal (or CI), fatal warnings are strictly enforced.
SBT automatically includes a file located at ~/.sbt/1.0/global.sbt
. We can use it to conditionally tweak compiler flags.
[~/.sbt/1.0/global.sbt]
(sys.props.contains("idea.managed")) match {
case true =>
println(true)
scalacOptions in Compile --= Seq("-Xfatal-warnings")
case false => scalacOptions --= Seq()
}
This disables -Xfatal-warnings
only inside IntelliJ, so you can freely experiment. Reload the SBT project, and you’re all set.
Meanwhile, building from the command line (like in CI) still enforces the flag—preserving the quality gate.