SBT: Multi-Project Builds, Plugins & CI
Multi-Project Builds
lazy val core = project.in(file("core"))
lazy val server = project.in(file("server")).dependsOn(core)
// Splitting into sub-modules can also improve incremental build
// performance -- a change isolated to `server` doesn't trigger
// recompiling the unrelated `core` modulePlugins: Extending Core Capabilities
// project/plugins.sbt
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.1.1")sbt assembly
# Bundles the app AND all its dependencies into a single
# self-contained "fat" JAR -- simplifies deployment, no need to
# separately ship every individual dependency JARCross-Building for Multiple Scala Versions
crossScalaVersions := Seq("2.12.17", "2.13.10", "3.2.2")
// A library maintainer's own projects might use just one version,
// but publishing cross-built lets consumers on OTHER Scala versions
// actually use the library tooRunning in CI
# Single, non-interactive command with a clean exit code --
# fits CI's automated, scripted nature better than the interactive
# shell workflow more natural for local development
sbt testCustom Tasks
Since build.sbt is genuine Scala code, a team can define entirely custom build tasks with arbitrary logic (generating a version-info file, running a validation script) directly in their build definition -- not limited to a fixed, predetermined set of built-in commands.
Build Time as a Real Concern
Scala's compiler has a reputation for being comparatively slower than some other languages', due partly to its sophisticated type-system analysis. For large codebases, teams actively invest in module splitting and leveraging incremental compilation to keep iteration fast.
Keep your own version of these notes — editable, searchable, and organised by your stack.
Start free