Migrating to Scala 3

Tue, 08 Jul 2025 12:43:00 +0000
Scala, Migration, Scala 3

There are several ways to migrate your existing Scala 2 project to Scala 3. You could start with implementing a new separate service in Scala 3 which is called a Parallel Migration. This approach has a few pros and cons however, with significant hurdles being the need to manage two codebases temporarily and the possibility of APIs being inconsistent if not handled carefully. It can be a challenging task, but sometimes it is the only way.

In this blog post, I’d like to explore alternative approaches for migrating an existing Scala 2 codebase to Scala 3 – options that might be a better fit for many of us planning the transition. With Scala 3 introducing exciting new features, it’s natural that we’re eager to take advantage of them! 🙂

Cross-Building a Scala 2 project with Scala 3

Migrating a project from Scala 2 to Scala 3 can be complex, particularly for large-scale applications. Instead of attempting a full migration in one step, cross-building (also known as cross-compile) allows for a gradual transition while ensuring that your codebase remains functional in both versions.

This method enables your project to compile with both Scala 2 and Scala 3, allowing incremental changes, continuous verification, and smoother dependency transitions before entirely switching to Scala 3.

What is Cross-Building?

Cross-building refers to compiling the same codebase against multiple Scala versions. This is particularly useful in situations where you need to:

Test Compatibility

Ensure that your Scala 2 code remains valid under Scala 3.

Detect potential breaking changes before full migration.

Migrate Incrementally

Refactor the codebase progressively while maintaining compatibility.

Reduce migration risks for large projects.

Adapt to Library Support

Some dependencies may not yet support Scala 3.

Cross-building allows you to continue using Scala 2 while waiting for library updates.

Setting up cross-building in SBT

To enable cross-building in SBT, update your build.sbt with the following:

ThisBuild / crossScalaVersions := Seq("2.13.20", "3.3.6")
ThisBuild / scalaVersion := "2.13.20" // Default Scala version

Note: Using Scala LTS e.g. 3.3.x, gives you less warnings and a more incremental approach-- advice based on experience from VirtusLab developers.

Compiling for Different Versions

Once cross-building is configured, you can compile and test your project with different Scala versions:

sbt ++2.13.20 compile  # Compile using Scala 2.13
sbt ++3.3.6 compile    # Compile using Scala 3

SBT will automatically determine which source files and dependencies apply to each version.

Handling Scala 2 and Scala 3 differences

While many Scala 2 features are compatible with Scala 3, some may require adaptation. You can create version-specific directories for such cases:

src/main/scala      -> Shared code (works in both Scala 2 and Scala 3)
src/main/scala-2    -> Code compiled only in Scala 2
src/main/scala-3    -> Code compiled only in Scala 3

SBT will automatically select the appropriate source files based on the active Scala version.

Example: Symbol Literal Syntax

Scala 2 supports symbol literals (e.g.,'mySymbol), but they were removed in Scala 3. You can isolate such usages to maintain compatibility:...