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

Featured image

Photo by Alex on Unsplash

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:

Demo of the problem in code

We can’t even run our little experiment without resolving all warnings first. Frustrating!

Error in IDE

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.