Scala 3 Module 1 – Start here: Scala 3.9 LTS on the JVM
What Scala 3 is and where it runs in 2026
What Scala 3 compiles to and where it runs, the Scala Next and Scala LTS release lines from 3.3 to 3.10, and how to read the versions behind a run.
What you will learn
- Describe where Scala 3 code runs, on the JVM, as JavaScript through Scala.js and as native programs
- Explain the Scala Next and Scala LTS release lines and choose one for a new application or library
- Apply the rule that decides which Scala versions can use code compiled with another version
- Identify the Scala and JDK versions behind a recorded run from what it printed
What you’ll learn
What you will learn
- Describe where Scala 3 code runs, on the JVM, as JavaScript through Scala.js and as native programs
- Explain the Scala Next and Scala LTS release lines and choose one for a new application or library
- Apply the rule that decides which Scala versions can use code compiled with another version
- Identify the Scala and JDK versions behind a recorded run from what it printed
On this page
Scala 3 is a statically typed language that combines object-oriented and functional programming, and its main home
is the Java Virtual Machine. When Scala 3.9.0 compiled the small program on this page, it wrote five files: three
.class files that any JDK 17 or newer can run, and two .tasty files that keep Scala’s own view of the types. The
same language also compiles to JavaScript and to native programs.
Scala ships on two release lines, and every version this track mentions, from 3.3 to 3.10, belongs to one of them. This lesson shows where Scala code runs, how to choose a version for an application or for a library, and how to read the versions behind each recorded run in this track.
Scala compiles to class files that any JDK 17 or newer runs
Here is a whole Scala 3 program. @main marks the method that starts it, and the method prints the version of the
Scala library that runs it:
// A whole Scala 3 program: @main makes the method hello the program's starting point.
@main def hello(): Unit =
val version = scala.util.Properties.versionNumberString
println(s"Hello from Scala $version") The session below compiles it into a folder named classes, lists what the compiler wrote, and looks inside with
javap, a tool that comes with every JDK: The Java Development Kit: a JVM together with the Java compiler and tools such as javap. Scala 3.8 and later need JDK 17 or newer to compile and run. More in “Install Scala 3.9, a JDK and an editor”. Each session in this track shows the commands after
$ and what they printed. They run with --server=false, which starts a fresh compiler for each command instead of
using a background compile server (the third lesson explains the difference), and on JDK 25 they leave out a few
WARNING lines that the scala launcher prints about itself, which the next lesson shows and explains.
from session import run
# Compile hello.scala into the folder classes, list what the compiler wrote, then look inside with the JDK's javap.
run("""
cd hello
scala compile . --server=false -d classes
ls classes
javap -cp classes hello
javap -v -cp classes hello | grep "major version"
""") Output
$ cd hello
$ scala compile . --server=false -d classes
$ ls classes
hello.class
hello.tasty
hello$package.class
hello$package.tasty
hello$package$.class
$ javap -cp classes hello
Compiled from "hello.scala"
public final class hello {
public hello();
public static void main(java.lang.String[]);
}
$ javap -v -cp classes hello | grep "major version"
major version: 61
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 compile_session.py
The compiler wrote five files. The three .class files hold bytecode: The instructions in a .class file. They are written for the JVM rather than for a real processor, so one compiled file runs on Windows, macOS and Linux alike., the instruction set
of the JVM: The Java Virtual Machine: the program that loads compiled class files and runs their bytecode on your computer's processor. Scala's main platform; every JDK includes one.: hello.class is the program’s starting point, and the two hello$package classes
hold the file’s top-level definitions. javap shows that hello is an ordinary JVM class with the
public static void main(java.lang.String[]) method that every Java program starts from, generated from the @main
method. So a Scala program can call any Java library, and Java code can call Scala classes, because both end up as the
same kind of class file.
The line major version: 61 is the class-file version of Java SE 17 in the JVM specification. A JVM runs class files
up to its own version, so JDK 17, 21 and 25 all run this program, and an older JDK refuses it. That is why Scala 3.8
and later need JDK 17 or newer, to compile and to run.
The two .tasty files hold TASTy: The typed syntax trees that the Scala 3 compiler writes into .tasty files beside the .class files. Later compilers and tools read them to see the full Scala types of compiled code., the typed trees the compiler built while it checked the
program. Bytecode loses part of the types: a List[Int] becomes a list of plain objects, and the JVM has no idea of a
union type such as Int | String. TASTy keeps them all, so when another Scala project uses this code as a library,
its compiler, and editors such as Metals, read the full Scala types from these files. Step through the same compile
and watch which program reads which file:
The drawing shows the whole scene; its 6 steps are listed below.
Every step as text
- The source. hello.scala holds one method marked @main. It prints a greeting with the version of the Scala library that runs it.
- The compiler checks it. scala compile hands the file to the Scala 3.9.0 compiler. It checks every type first; only a program that passes is translated, and a mistake stops it here with an error.
- Bytecode for the JVM. It writes three class files of JVM bytecode: hello.class, the program's starting point with a static main method, and two hello$package classes for the file's top-level definitions.
- The types, kept in TASTy. It also writes two .tasty files. They keep the typed trees the compiler built, with the full Scala types that bytecode loses, such as the element type of a List or a union type.
- A JVM runs the bytecode. Class-file version 61 is Java SE 17. A JVM runs class files up to its own version, so JDK 17, 21 and 25 run hello, and JDK 11 refuses it.
- The next compiler reads TASTy. When another Scala project uses this code as a library, its compiler reads the .tasty files rather than the bytecode, and so does an editor such as Metals when it shows types and completions.
Key idea
The compiler turns Scala source into JVM bytecode in .class files, which any JDK 17 or newer runs, and keeps the
full Scala types in .tasty files for the next compiler and the editor.
Checkpoint
Check: What the compiler writes
The same code can also become JavaScript or a native program
The JVM is the main platform and the one this track uses, but two other projects compile the same language for other places. Scala.js turns Scala into JavaScript for browsers and Node.js, and Scala Native compiles it ahead of time, through LLVM, into a program that the operating system runs directly, with its own small runtime and no JVM:
| Platform | What the compiler produces, and what runs it | A typical use |
|---|---|---|
| JVM (the default) | .class and .tasty files, run by a JDK 17 or newer |
back-end services, data jobs, command-line tools |
| Scala.js 1.22 | JavaScript, run by a browser or Node.js | web front ends written in Scala |
| Scala Native 0.5 | a native executable, run by the operating system | small tools that must not need a JVM |
On the JVM, Scala code can use every Java library. Apache Spark, the data-processing engine, is one example of Scala there: it offers a Scala API alongside Java, Python and R, and Spark 4.2.0 runs on Scala 2.13. Its documentation says that a Scala application must use the same Scala version that Spark was compiled for, which is why the Spark module of this track pins Scala 2.13 while every other module uses Scala 3.9.
Scala is a smaller language than Java or Python. In the 2025 Stack Overflow Developer Survey, 2.6 % of the 31,771 respondents to the question on programming languages chose Scala, and so did 2.8 % of the professional developers among them.
Learn the JVM version first
Learn and test on the JVM first, even for code that will also run as JavaScript or natively. Every library and tool of this track works there, and the same source can target the other platforms later.
Checkpoint
Check: JVM, Scala.js and Scala Native
Scala Next moves forward, Scala LTS stays put
Scala 3 is released on two lines. The official download page lists Scala Next: The release line of Scala 3 that receives new language features, in regular minor releases such as 3.10 after 3.9., currently 3.10.0, as “the default to be used by most users”, and Scala LTS: A long-term support line of Scala 3, such as 3.3 or 3.9. It receives only patch releases with fixes, for at least three years, and libraries are usually published with it., 3.9.0, as “advised to be used for publishing libraries”. Next is where the language develops: it receives regular minor releases, 3.8, 3.9 and 3.10, each with new features. An LTS line, in the words of the compatibility plan, “will only receive patch updates”: 3.9.1, 3.9.2 and so on, with fixes but no new features, for at least three years.
The project may name a new LTS line from the Next releases only after more than two years. 3.3 was the first, and 3.9 is the second. The Scala 3.9 announcement promises 3.9 at least three years of maintenance and keeps 3.3 LTS maintained for one more year after 3.9.0, with fewer fixes. Walk through the releases that shaped what a new project should use:
- 3.0
Scala 3 is released new language
The first release of Scala 3 came out in 2021, the third version of the language, with a new compiler and a cleaner syntax that makes braces optional.
- 3.3 LTS
The first long-term support line LTS
In 2023 the 3.3 series became the first LTS line of Scala 3. Its latest patch release is 3.3.8, and it stays maintained for one year after 3.9.0, with fewer fixes.
- 3.5
Scala CLI becomes the scala command tooling
From 2024 the scala command of the Scala distribution is Scala CLI, which compiles, runs, tests and packages code and opens the REPL, so a separate build tool is no longer needed to start.
- 3.8
JDK 17 or newer is required JDK 17
In 2026 the standard library began to be compiled with Scala 3, the REPL became a separate artifact with pretty-printed results, and compiling and running Scala now need JDK 17 or newer.
- 3.9 LTS
The second long-term support line LTS
3.9.0 opened the second LTS line in 2026, with at least three years of maintenance. It made the into modifier for implicit conversions stable, and this track's examples run on it.
- 3.10
Scala Next moves on Next
3.10.0 continued the Next line after 3.9. It turns old patterns into errors, such as the with type operator and this in top-level definitions, and lets the REPL load libraries with //> using dep.
The timeline has 6 events, from 3.0 to 3.10.
3.10 notes in this track
The lessons of this track run on 3.9.0. Where Scala 3.10 behaves differently, a note says so: 3.10.0 makes the
with type operator an error, forbids this in top-level definitions and stops finding implicit values in
companion objects that are not accessible, which 3.9 still finds and warns about under -deprecation.
Checkpoint
Check: Scala Next and Scala LTS
A newer Scala reads older output, never the other way round
Releases on one line are compatible in both directions: code compiled with 3.9.0 works with 3.9.2, and the other way
round. Between minor versions only one direction works. The compatibility plan puts it as a rule: “Scala 3.b.y can
consume the output of Scala 3.a.x only if b is greater or equal to a.”
Work it through for a library. Compiled with 3.3.8, it can be used by projects on 3.3, 3.9 and 3.10, because each of those minor versions is 3 or more. Compiled with 3.9.0, it reaches projects on 3.9 and 3.10, but a project still on 3.3 cannot read it, and the 3.9 announcement warns library authors of exactly that. Compiled with 3.10.0, it shuts out every 3.9 project too. An application has no such users, so it can take the newest version.
Answer the questions about your own project, and the table marks the versions that fit:
Answer the questions above: the table then marks what fits your case and says why the rest does not.
| Version | Newest language features | Who can use what it compiles | JDK the output needs | How long it gets fixes |
|---|---|---|---|---|
| 3.10.0 Release line: Scala Next: new features first | Good gets them first, and also turns some old patterns into errors | Poor only projects on 3.10 or newer | Mixed JDK 17 or newer | Mixed superseded by the next minor release, which you then move up to |
| 3.9.0 Release line: Scala LTS: fixes for at least three years | Mixed everything up to 3.9, nothing newer | Good every project on 3.9 or newer, Scala Next included | Mixed JDK 17 or newer | Good patch releases for at least three years |
| 3.3.8 Release line: the first LTS line, maintained one more year | Poor nothing added from 3.4 to 3.10 | Good every Scala 3 project from 3.3 on | Good its output still runs on JDK 8 | Poor maintained for one year after 3.9.0, with fewer fixes |
How the answers decide
- “Will other projects add what you build as a library dependency?” Yes, it is a library: rules out 3.10.0. Code compiled with 3.10 can be used only from 3.10 on, so a library built with it shuts out every project on 3.9 LTS.
- “Will other projects add what you build as a library dependency?” No, it is an application or a service: adds a caveat to 3.3.8. An application has no users that compile against it, so it can take the newest line; 3.3 only adds a deadline to move up.
- “Must projects that still build with Scala 3.3 be able to use it?” Yes, some of them are on 3.3: rules out 3.10.0 and 3.9.0. A 3.3 compiler cannot read the output of 3.4 or later, so only 3.3.8 reaches projects that still build with 3.3.
- “Must the compiled code run on a JDK older than 17?” Yes, on JDK 8 or 11: rules out 3.10.0 and 3.9.0. From 3.8 on, Scala needs JDK 17 or newer to compile and to run; the 3.3 line still produces bytecode for JDK 8.
In short: an application can follow Scala Next, or stay on 3.9 LTS for fewer upgrades; a library is published with 3.9 LTS, or with 3.3.8 while its users still build with 3.3; and code that must still run on a JDK older than 17 stays on the 3.3 line, the only maintained line that still targets older JDKs, because from 3.8 on the compiler needs JDK 17.
Checkpoint
Check: Which versions can use which
Every recorded run in this track names its versions
The programs of this track do not run in your browser. Each one shows what it printed when it ran with Scala 3.9.0 on
Eclipse Temurin 25.0.4.1, under the command that ran it, so you can type the same command on your own computer and
compare. This session prints the versions behind every run: first the scala command’s own report, then a program
that asks the running JVM:
// Reports what runs this program: the Scala library, the Java runtime and the platform.
@main def versionReport(): Unit =
val scalaVersion = scala.util.Properties.versionNumberString
val javaVersion = System.getProperty("java.version")
val vendor = System.getProperty("java.vendor")
println(s"Scala library $scalaVersion")
println(s"Java runtime $javaVersion ($vendor)")
println(s"Platform ${System.getProperty("os.name")}, ${System.getProperty("os.arch")}") from session import run
# The versions behind the recorded runs of this track: the scala command, then a program that reports what runs it.
run("""
scala version
cd report
scala run . --server=false
""") Output
$ scala version Scala code runner version: 1.16.0 Scala version (default): 3.9.0 $ cd report $ scala run . --server=false Scala library 3.9.0 Java runtime 25.0.4.1 (Eclipse Adoptium) Platform Mac OS X, aarch64
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 report_session.py
Read the lines from the top. Scala code runner version: 1.16.0 is the version of the tool behind the scala
command: since Scala 3.5 that command is Scala CLI, which compiles, runs, tests and packages code. Scala version (default): 3.9.0 is the compiler it uses when a project names no version. The program then reports the Scala library
it found, 3.9.0, the Java runtime, Temurin 25.0.4.1 from the Eclipse Adoptium project, and the platform, macOS on an
Arm processor (aarch64).
Two numbers can disagree on your computer, and both are worth knowing. The scala command may default to a newer
Scala than the one a project asks for, and the JDK that runs your program may not be the first java on your PATH.
The next lesson shows how each is chosen and how to pin them.
Checkpoint
Check: Reading the versions of a run
Practise: versions and platforms
Fifteen questions cover the five ideas above, from the files the compiler writes to the versions of a run. The session starts with whichever idea your answers show to be weakest, and each answer comes with its reason:
Practice that adapts to you
Practise this lesson
15 questions about 5 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.
Interview questions
Fresher · What is the difference between Scala Next and Scala LTS, and which would you use for a new library?
Scala Next is the line where the language develops. It gets regular minor releases with new features, such as 3.8, 3.9 and 3.10, and each one supersedes the one before. Scala LTS is a long-term support line, first 3.3 and now 3.9: it gets only patch releases with fixes, for at least three years. The two matter most for libraries, because a Scala 3 minor version can use code compiled with the same or an older minor version, never a newer one. A library compiled with 3.10 can be used only from 3.10 on, while one compiled with 3.9 reaches every 3.9 and 3.10 project. So a new library is published with 3.9 LTS, or with 3.3.8 while important users still build with 3.3. An application has no downstream users, so it can follow Scala Next.
Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.
-
Mid to senior
Your team maintains an internal library used by services on Scala 3.3.8 and on 3.9.0. A developer wants to publish the next release compiled with 3.10.0 to use a new language feature. What do you decide, and how do you roll it out?
-
Mid to staff
A company writes Spark 4.2 jobs and back-end services in Scala and wants one shared module of domain code. Why can't both sides simply use Scala 3.9, and how would you structure the shared module?
Key takeaways
- Scala 3 compiles to JVM bytecode in
.classfiles, which any JDK 17 or newer runs, plus.tastyfiles that keep the full Scala types for later compilers and editors. - The JVM is Scala’s main platform, where it can use every Java library; Scala.js compiles the same language to JavaScript and Scala Native to native programs.
- Scala Next, 3.10, receives new features in minor releases, while an LTS line, 3.3 and now 3.9, receives only fixes for at least three years.
- A Scala 3 minor version can use code compiled by the same or an older one, never a newer one, so libraries are published with 3.9 LTS and applications may follow Scala Next.
- The runs of this track used Scala 3.9.0, whose
scalacommand is Scala CLI 1.16.0, on Temurin 25.0.4.1; the Spark module uses Scala 2.13, the version Spark is built for.
Terms in this lesson
- bytecode
- The instructions in a .class file. They are written for the JVM rather than for a real processor, so one compiled file runs on Windows, macOS and Linux alike.
- JVM
- The Java Virtual Machine: the program that loads compiled class files and runs their bytecode on your computer's processor. Scala's main platform; every JDK includes one.
- Scala LTS
- A long-term support line of Scala 3, such as 3.3 or 3.9. It receives only patch releases with fixes, for at least three years, and libraries are usually published with it.
- Scala Next
- The release line of Scala 3 that receives new language features, in regular minor releases such as 3.10 after 3.9.
- TASTy
- The typed syntax trees that the Scala 3 compiler writes into .tasty files beside the .class files. Later compilers and tools read them to see the full Scala types of compiled code.
References
- Download Scala (Scala Next, Scala LTS and installation) (The Scala Programming Language (scala-lang.org))
- Scala 3.9 announcement (the new LTS line) (The Scala Programming Language (scala-lang.org))
- Scala 3.8 announcement (JDK 17 and the standard library compiled with Scala 3) (The Scala Programming Language (scala-lang.org))
- Long-term compatibility plans for Scala 3 (The Scala Programming Language (scala-lang.org))
- Scala 3.10.0 release notes (The Scala 3 project (GitHub))
- Scala 3.5.0 released (Scala CLI becomes the default runner) (The Scala Programming Language (scala-lang.org))
- Scala 3.3.0 released (the first LTS series) (The Scala Programming Language (scala-lang.org))
- Scala 3 is here (The Scala Programming Language (scala-lang.org))
- TASTy overview (Scala 3 guides) (Scala Center and EPFL (docs.scala-lang.org))
- Scala 3 Book: Scala for Java developers (Scala Center and EPFL (docs.scala-lang.org))
- The Java Virtual Machine Specification, Java SE 25: chapter 4, The class File Format (Oracle)
- The javap command (JDK 25 tool reference) (Oracle)
- Scala.js (The Scala.js project)
- Scala Native documentation (The Scala Native project)
- Spark 4.2.0 overview (supported Java and Scala versions) (The Apache Software Foundation)
- 2025 Stack Overflow Developer Survey, Technology (Stack Overflow)
Report a problem with this lesson
Kept only in this browser. Your Learn progress