- Published on
Modern Scala 2026 — Scala 3.6 / 3.7 / ZIO 2 / Cats Effect 3 / Pekko / Tapir / Mill / Scala CLI Deep Dive
- Authors

- Name
- Youngju Kim
- @fjvbn20031
Table of Contents
1. Modern Scala in 2026 — The Scala 3 Era
Scala was once called "that weird language." Even in the early 2020s, people would say "Scala is hard," "Scala 2 compiles slowly," "the learning curve is too steep to roll out across a team." Scala in 2026 is a different beast. Scala 3 has settled in, build tools have diversified, effect systems like ZIO and Cats Effect have matured, and Pekko has absorbed the Akka BSL license drama. At the same time, Scala ideas are being re-exported into TypeScript via projects like Effect.ts.
This article is a deep dive into the Scala ecosystem as of May 2026.
The Big Currents Around Scala in 2026
| Current | Detail |
|---|---|
| Scala 3 stabilization | Scala 3.6 (Dec 2024) / 3.7 (Mar 2025) stable, Scala 4 preview in progress |
| Build tool diversification | sbt 2.x (slow transition), Mill (Li Haoyi), Scala CLI (scripts and POCs) |
| Effect system split | ZIO 2 vs Cats Effect 3 — two camps coexist |
| Akka aftermath | Akka moved to BSL (2022), Apache Pekko forked — now the de facto standard |
| Web framework maturity | http4s, Tapir, Play Framework 3 — a functional web stack |
| Other platforms | Scala.js (JS), Scala Native 0.5 (native binaries) |
| Data engineering | Apache Spark's core language — still a huge market |
| Idea exports | Effect.ts (TS), Iron (refined types), magnolify |
| Adoption | Lichess, parts of Toss, MegaZone, Septeni, Money Forward, etc. |
Why Scala Has Not Vanished
People have called "the end of Scala" several times. The 2026 Scala has a clear seat at the table.
- Spark is effectively written in Scala — the core of data engineering and ML pipelines
- The language that best breaks through JVM expressiveness limits — deeper FP than Kotlin, stronger types than Java
- Effect systems (ZIO / Cats Effect) actually work — industry-proven FP stacks
- The cutting edge of type safety via Iron's refined types — other languages have not caught up
- Cases like Lichess where a single shop runs millions of users — proof of Scala's real-world capability
Let's go through each piece.
2. Scala 3.6 (Dec 2024) / 3.7 (Mar 2025) — New Features
Scala 3 has been steadily evolving since its May 2021 launch. The 3.6 and 3.7 releases at the end of 2024 and the start of 2025 sent the signal "Scala 3 is truly stable."
Major Changes in Scala 3.6
In Scala 3.6 (December 8, 2024) the highlights are:
- Quotes API stabilization — macro authoring is much easier
- Named tuples (experimental) — labels for tuple fields,
(name = "Alice", age = 30)instead of("Alice", 30) - Type ascription warnings — warnings for ambiguous type ascriptions
-Wunusedimprovements — refined warnings for unused imports and vars- Capture checking progress — preview of a key Scala 4 feature
// Named tuples (experimental in 3.6, official in 4.0)
val person = (name = "Alice", age = 30)
val n: String = person.name // access by field name
val a: Int = person.age
// Function arguments as named tuples too
def greet(p: (name: String, age: Int)): String =
s"Hello $p.name, $p.age years old"
Changes in Scala 3.7
Scala 3.7 (March 27, 2025) is a minor release but full of meaningful improvements.
@experimentalannotation cleanup — clearer distinction between stable and experimental features- Best-effort compilation — IDE auto-complete works even with some errors
- Compile speed improvements — the Scala 2 era reputation continues to fade
- JDK 25 compatibility — works with the latest LTS
- New inline API — fine-grained control over
inline def
Compile Speed — The Biggest Complaint Is Fading
Scala 2.x had a notorious reputation for slow compiles. Scala 3.7 is faster thanks to:
| Technique | Effect |
|---|---|
| Incremental compilation (Zinc) improvements | Only changed files recompile |
| Pickle cache | Type info reuse |
| Parallel compilation | Module-level parallelism |
Using bloop | Background compile server |
It's still slower than Java or Kotlin, but the "go grab a soda while it compiles" days are over.
Migration Tooling
Scala 2 to 3 migration is automated by:
# Scalafix - automated refactoring
sbt "scalafixAll dependency:Scala3RewriteFromScala2Old@org.scalameta:scalafix-rules:0.13.0"
# Scala 3 compiler's auto-migration mode
scalac -source:3.0-migration MyFile.scala
# scala-migrate CLI
cs install scala-migrate
scala-migrate migrate-build
Most Scala 2 code resolves to 80-90% automatic migration.
3. Scala 4 Preview — What's Next
Scala 4 hasn't shipped yet, but the preview has been active since late 2025. The theme is "promote features that entered Scala 3 as experimental into official status."
What Scala 4 Aims For
- Capture checking goes official — tracking mutable resources, memory safety
- Named tuples go official — lightweight record types
- Match types improvements — type-level pattern matching
- Implicit system simplification — moving toward just
givenandusing - Compiler internals cleanup — TASTy format stabilization
What Is Capture Checking
This is the biggest change that entered Scala 3.x as experimental and goes official in Scala 4. The type system tracks "what resources a function captures."
// Scala 4 preview
import scala.language.experimental.captureChecking
// Declare that f captures the io resource
def withFile[T](path: String)(f: File^ => T): T = ???
// ^ capability
// The compiler guarantees captured resources don't leak outside
val data = withFile("a.txt") { file =>
file.readAll()
}
// Compile error: cannot return file
val leaked = withFile("a.txt") { file => file } // ERROR
This is a similar idea to Rust's borrow checker but runs on the JVM. It's essentially handling ZIO/Cats Effect Resource at the language level.
Scala 4 Release Timing
EPFL (where Scala was born) and the Scala Center have officially targeted "late 2026 to early 2027." That said, Scala follows a policy of "don't break compatibility just because the major version changes," so even when Scala 4 lands, most Scala 3 code will keep building.
4. sbt 2.x / Mill / Scala CLI — Build Tool Evolution
For a long time the Scala build tool was sbt. Powerful, but with a steep learning curve and notoriously painful debugging when something broke. The 2026 landscape looks different.
sbt 2.x — Slow Transition
sbt 2.x has been in RC since 2024, and as of May 2026 it still hasn't fully replaced 1.x. The main changes:
- Build definitions can be written in Scala 3
- New plugin model — fewer dependency conflicts
TaskAPI cleanup- Compile/run separation — better incremental builds
// build.sbt (sbt 2.x style)
ThisBuild / scalaVersion := "3.7.0"
ThisBuild / version := "0.1.0-SNAPSHOT"
lazy val root = (project in file("."))
.settings(
name := "my-app",
libraryDependencies ++= Seq(
"dev.zio" %% "zio" % "2.1.10",
"org.scalatest" %% "scalatest" % "3.2.19" % Test
)
)
Mill — Li Haoyi's Alternative
Mill is a build tool by Li Haoyi. For people tired of sbt's "DSL magic," it defines builds as plain Scala objects.
// build.sc
import mill._, scalalib._
object app extends ScalaModule {
def scalaVersion = "3.7.0"
def ivyDeps = Agg(
ivy"dev.zio::zio:2.1.10"
)
object test extends ScalaTests {
def ivyDeps = Agg(ivy"org.scalatest::scalatest:3.2.19")
def testFramework = "org.scalatest.tools.Framework"
}
}
Mill's strengths:
- Explicit — build logic is just Scala methods. No magic
- Fast — change detection is more accurate
- Understandable errors — easier to debug than sbt
Parts of Twitter and Databricks have reportedly migrated to Mill.
Scala CLI — The Right Answer for Scripts
Scala CLI is the official tool from the Scala Center, and it completely replaces sbt for small scripts, POCs, and learning.
# Install
brew install Virtuslab/scala-cli/scala-cli
# Single-file script
scala-cli run hello.scala
# Declare dependencies inside the code
cat > script.scala <<'EOF'
//> using scala 3.7.0
//> using dep dev.zio::zio:2.1.10
import zio._
object Main extends ZIOAppDefault:
def run = Console.printLine("Hello from Scala CLI")
EOF
scala-cli run script.scala
# Package as a single native binary (via Scala Native)
scala-cli package --native --output myapp script.scala
Since 2024, Scala CLI has been the official backend of the scala command. Run scala myfile.scala and Scala CLI runs underneath.
Build Tool Comparison
| Item | sbt 2.x | Mill | Scala CLI |
|---|---|---|---|
| Target | Large projects | Medium-large | Scripts and small apps |
| Build definition | build.sbt (DSL) | build.sc (Scala objects) | using directives |
| Learning curve | Steep | Medium | Almost none |
| Speed | Average | Fast | Very fast |
| Plugin ecosystem | Very rich | Average | Limited |
| Recommended for | Existing sbt projects | New projects | Learning and POCs |
5. ZIO 2 — The Functional Effect Camp
ZIO is the most popular functional effect library in Scala. As of May 2026, ZIO 2.1.x is the standard, and the ZIO 3 roadmap is public.
ZIO's Core Idea — ZIO[R, E, A]
A ZIO value carries three type parameters:
R(Environment) — dependencies required for the effect to runE(Error) — the error type the effect can fail withA(Success) — the value type returned on success
import zio._
// Returns Int but may fail with Throwable
val task1: ZIO[Any, Throwable, Int] = ZIO.attempt(42)
// Requires Database, can fail with AppError, returns User
val task2: ZIO[Database, AppError, User] =
ZIO.serviceWithZIO[Database](_.getUser(123))
// Composition
val task3: ZIO[Database, AppError, String] =
for {
user <- task2
_ <- Console.printLine(s"Got user: $user.name").orDie
} yield user.name
ZIO Layer — Dependency Injection
ZIO's most powerful piece is the Layer system — a functional take on Spring DI.
trait Database:
def getUser(id: Int): Task[User]
class LiveDatabase(config: DbConfig) extends Database:
def getUser(id: Int): Task[User] = ZIO.attempt(???)
object LiveDatabase:
val layer: ZLayer[DbConfig, Nothing, Database] =
ZLayer.fromFunction(LiveDatabase(_))
// Compose
val appLayer = DbConfig.layer >>> LiveDatabase.layer
New in ZIO 2
Scopefor resource management —ZManagedwas removed for a simpler APIZStreamstabilization — back-pressured stream processingZIO.attemptBlocking— automatic isolation of blocking workZIO.logSpan— distributed tracing integration
ZIO Ecosystem
| Library | Role |
|---|---|
| zio-http | HTTP server and client |
| zio-json | JSON serialization |
| zio-config | Configuration |
| zio-quill | Compile-time SQL generation |
| zio-kafka | Kafka integration |
| zio-prelude | Functional data types |
| zio-schema | Schema-based serialization |
6. Cats Effect 3 — The Other Functional Camp
Cats Effect is the other pillar of Scala functional effects alongside ZIO. Built by the Typelevel camp (Cats, Cats Effect, http4s, doobie, etc.), it centers on a simple IO[A] type.
IO[A] — The Beauty of Simplicity
import cats.effect._
import cats.implicits._
val program: IO[String] = for {
_ <- IO.println("Enter your name:")
name <- IO.readLine
_ <- IO.println(s"Hello, $name!")
} yield name
object Main extends IOApp.Simple:
def run: IO[Unit] = program.void
ZIO vs Cats Effect — How They Differ
| Item | ZIO 2 | Cats Effect 3 |
|---|---|---|
| Core type | ZIO[R, E, A] | IO[A] |
| Error tracking | Explicit in the type (E) | All Throwable |
| Dependency injection | Layer system | typeclasses + Resource |
| Learning curve | Steep (3 type parameters) | Gentle (1 parameter) |
| Ecosystem | zio-* series | Typelevel camp |
| Philosophy | "All together inside ZIO" | "Compose small libraries" |
Cats Effect's Strengths
- Typeclass-based — decomposed into
MonadCancel,Concurrent,Async Fiberfor concurrency — lightweight green threadsResource[F, A]for resource management — functional try-with-resources- Loom compatible — composes naturally with JDK 21+ virtual threads
// Safe resource management with Resource
val dbResource: Resource[IO, Connection] =
Resource.make(IO(openConnection()))(c => IO(c.close()))
val program: IO[Unit] =
dbResource.use { conn =>
IO(conn.query("SELECT 1"))
}
// conn.close() runs even on exception
Picking a Camp in Practice
In the field these patterns are common:
- New teams → ZIO 2 (rich learning material, natural DI)
- Teams already on Typelevel → Cats Effect 3
- Neither → start with
Futureor synchronous code and adopt later if needed
It has religious-war energy, so just go with whatever the team agrees on.
7. Pekko (Apache, Akka Fork) — After the BSL Drama
The most political and shocking event in the Scala ecosystem was Akka's license change in September 2022.
Akka Going BSL
Lightbend (Akka's creator) announced on September 7, 2022, that Akka 2.7+ would move to BSL (Business Source License). The key conditions:
- Free for non-commercial use
- Commercial use is paid above a certain revenue threshold per year
- Converts to Apache 2.0 after three years (delayed open source)
This shocked the Scala community. Akka was the backbone of Play Framework, Lagom, Spark Streaming, and countless companies. Lightbend's position was "the company has to survive for the library to survive," but the community reacted with "trust is broken."
The Birth of Apache Pekko
In late 2022, Pekko launched under the Apache Software Foundation. It's a fork of Akka 2.6.x and 100% Apache 2.0 licensed.
// Akka (BSL)
import akka.actor.typed._
import akka.actor.typed.scaladsl._
// Pekko (Apache 2.0) - nearly identical
import org.apache.pekko.actor.typed._
import org.apache.pekko.actor.typed.scaladsl._
object Greeter:
sealed trait Command
case class Greet(name: String) extends Command
def apply(): Behavior[Command] = Behaviors.receive { (ctx, msg) =>
msg match
case Greet(name) =>
ctx.log.info(s"Hello $name")
Behaviors.same
}
The only difference is the package name akka.* to org.apache.pekko.*. Migration is a one-line sed.
The 2026 Reality
- New projects are nearly 100% Pekko — no commercial license risk
- Existing Akka users are still migrating to Pekko — Lightbend's revenue recovered with BSL, but trust did not
- Major Pekko releases are active — the community is moving on its own
- Play Framework 3 is also Pekko-based
Where Pekko Is Used
- Distributed systems — actor-model based large-scale processing
- Streaming — Pekko Streams, Pekko Connectors (Kafka, Cassandra, etc.)
- HTTP server — Pekko HTTP
- Event sourcing — Pekko Persistence
For event-driven systems in 2026, Pekko is the de facto standard.
8. Play Framework 3 / http4s / Tapir — Web
The Scala web framework world settles into three camps.
Play Framework 3 — The Full-Stack Framework
Play 3 is a full-stack MVC framework like Rails. It was rebuilt on Pekko in late 2023.
// Play 3 controller
package controllers
import javax.inject._
import play.api.mvc._
@Singleton
class HomeController @Inject() (cc: ControllerComponents)
extends AbstractController(cc):
def index() = Action { implicit request =>
Ok("Hello from Play 3")
}
def user(id: Int) = Action.async { implicit request =>
userService.get(id).map(u => Ok(Json.toJson(u)))
}
Pros — full-stack, template engine, form validation, asset pipeline all included. Cons — "not very functional," steep learning curve.
http4s — Functional Web
http4s is a purely functional web server built on Cats Effect.
import cats.effect._
import org.http4s._
import org.http4s.dsl.io._
import org.http4s.implicits._
import org.http4s.ember.server._
import com.comcast.ip4s._
val helloService = HttpRoutes.of[IO] {
case GET -> Root / "hello" / name =>
Ok(s"Hello, $name")
}.orNotFound
object Main extends IOApp.Simple:
def run = EmberServerBuilder
.default[IO]
.withHost(ipv4"0.0.0.0")
.withPort(port"8080")
.withHttpApp(helloService)
.build
.useForever
Pros — purely functional, clear behavior, natural Cats Effect integration. Cons — looks like magic at first, middleware authoring is tricky.
Tapir — Type-Safe API Definitions
Tapir is the Scala community's secret weapon. Define endpoints at the type level, and code, clients, and OpenAPI specs are generated automatically.
import sttp.tapir._
import sttp.tapir.generic.auto._
import io.circe.generic.auto._
import sttp.tapir.json.circe._
case class User(id: Int, name: String)
// Define endpoint as a value
val getUser =
endpoint
.get
.in("users" / path[Int]("id"))
.out(jsonBody[User])
.errorOut(stringBody)
// Serve via http4s
import sttp.tapir.server.http4s.Http4sServerInterpreter
val routes = Http4sServerInterpreter[IO]()
.toRoutes(getUser.serverLogic(id => IO.pure(Right(User(id, "Alice")))))
// Generate OpenAPI from the same definition
import sttp.tapir.docs.openapi.OpenAPIDocsInterpreter
val docs = OpenAPIDocsInterpreter().toOpenAPI(getUser, "My API", "1.0")
println(docs.toYaml)
Define an endpoint once and you get:
- HTTP server routes
- Type-safe clients
- OpenAPI / Swagger spec
- Automatic docs
It's the cutting edge of backend interface definition.
Which Should You Pick
| Situation | Recommendation |
|---|---|
| Full-stack web app (with templates) | Play 3 |
| Functional microservice | http4s + Tapir |
| Large API + schema-first | Tapir + any backend |
| Actor-based concurrency | Pekko HTTP |
| New team, low learning overhead | Tapir + http4s |
9. Scala.js / Scala Native 0.5 — Other Platforms
Scala isn't only a JVM language. You can target JavaScript via Scala.js and LLVM-based native binaries via Scala Native.
Scala.js — Compile to JavaScript
Scala.js compiles Scala to JavaScript. In 2026, 1.18.x is the standard, and interop with React, Vue, and other JS libraries is clean.
// Scala.js + Laminar (reactive UI)
import com.raquo.laminar.api.L._
val app = div(
h1("Hello Scala.js"),
button(
"Click me",
onClick --> { _ =>
println("Clicked!")
}
)
)
render(dom.document.getElementById("app"), app)
Pros — Scala's type system on the frontend, powerful dead-code elimination keeps bundles small. Cons — JS interop is possible but you sometimes need to write type definitions yourself.
Scala Native 0.5 — LLVM Native
Scala Native compiles Scala to LLVM IR and produces native binaries. Since 0.5.x (2024) it's truly usable.
// Hello, Scala Native
import scala.scalanative.unsafe._
import scala.scalanative.libc._
object Main:
def main(args: Array[String]): Unit =
stdio.printf(c"Hello from Scala Native! PID: %d\n", unistd.getpid())
# Compile
scala-cli package --native --output myapp Main.scala
# Run — starts instantly without the JVM
./myapp
Pros:
- No JVM startup time — ideal for CLI tools
- Small memory footprint — great for serverless like Lambda or Cloud Run
- Choose your GC — Boehm, Immix, Commix
- Call C libraries directly
Cons:
- Can't use all JVM libraries (reflection-dependent ones)
- Compilation through LLVM is slow
The Scala CLI + Scala Native combo is excellent for single-binary CLI tools. Tools like coursier and sbt-pack ship with this combination.
10. Spark Scala — Data Engineering
One of the biggest reasons Scala survived into 2026 is Apache Spark. Spark's core is written in Scala, and the Scala API is the fastest and most expressive.
Spark's Status
- Spark 3.5.x (2024) / Spark 4.0 (planned 2025)
- Python (PySpark) has the larger share, but high-performance and complex transformations still go to Scala
- Large data shops like Databricks and Palantir use Scala Spark as the main interface
Using Spark from Scala
import org.apache.spark.sql.SparkSession
import org.apache.spark.sql.functions._
val spark = SparkSession.builder
.appName("Example")
.master("local[*]")
.getOrCreate()
import spark.implicits._
case class Transaction(userId: Int, amount: Double, ts: String)
val df = Seq(
Transaction(1, 100.0, "2026-01-01"),
Transaction(1, 200.0, "2026-01-02"),
Transaction(2, 50.0, "2026-01-01")
).toDF()
val agg = df
.groupBy("userId")
.agg(
sum("amount").as("total"),
count("*").as("tx_count")
)
.orderBy(desc("total"))
agg.show()
Frameless — Type-Safe Spark
Frameless wraps Spark's DataFrame in a type-safe API and catches column-name typos at compile time.
import frameless._
import frameless.syntax._
val ts: TypedDataset[Transaction] = TypedDataset.create(transactions)
val totals: TypedDataset[(Int, Double)] = ts
.groupBy(ts('userId))
.agg(sum(ts('amount)))
.as[(Int, Double)]
// Compile error: there is no 'amounnt' column
val bad = ts.select(ts('amounnt)) // doesn't compile
Frameless 0.16+ migrated to Scala 3 and is stable.
Should Data Engineers Learn Scala
- PySpark owns ~60% of the market — you can start with Python
- But infra / platform teams are mostly Scala — Databricks itself is Scala
- Performance-critical transformations are clearly faster in Scala — direct JVM calls avoid serialization overhead
- Conclusion — data analysts → Python, data infra / platform engineers → Scala
11. Effect.ts — Scala Ideas Ported to TypeScript
A fascinating mid-2020s phenomenon is that Scala's ideas were re-exported into TypeScript. The poster child is Effect.ts.
What Is Effect.ts
Effect.ts ports the core ideas of ZIO and Cats Effect to TypeScript.
// Effect.ts (TypeScript)
import { Effect, Console } from "effect"
const program = Effect.gen(function* () {
yield* Console.log("Enter your name:")
const name = "Alice" // would be real input
yield* Console.log(`Hello, ${name}`)
return name
})
Effect.runPromise(program).then(console.log)
Compare with ZIO — practically the same structure:
// ZIO (Scala)
import zio._
val program = for {
_ <- Console.printLine("Enter your name:")
name <- ZIO.succeed("Alice")
_ <- Console.printLine(s"Hello, $name")
} yield name
Runtime.default.unsafeRun(program)
Why This Happened
- TypeScript's type system became expressive enough — generics, conditional types, and
inferlet you track effects - JS async hell — Promises and async/await hit a wall
- More FP-leaning users coming to TS — folks from Haskell and Scala backgrounds came to TS
- Demand for standardized error handling — patterns like
EitherandOptionbecame popular in TS too
What This Means for Scala
- Proof that Scala-born patterns transcend the language
- Scala users on full-stack TS gigs can reach naturally for Effect.ts
- Conversely, TS devs deep in FP can move naturally to Scala
The Scala community takes pride in this — "our ideas became mainstream."
12. Iron — Refined Types
Iron is a Scala 3 library that implements refined types — a tool for encoding constraints in types at compile time.
What Is a Refined Type
Encodes constraints like "an Int but positive" or "a String shaped like an email" into the type system.
import io.github.iltotore.iron._
import io.github.iltotore.iron.constraint.all._
// Positive integers
type PositiveInt = Int :| Positive
val age: PositiveInt = 30 // OK
val bad: PositiveInt = -5 // compile error
// Email
type Email = String :| Match["""[\w.+]+@[\w.]+"""]
val email: Email = "a@b.com" // OK
val notEmail: Email = "hello" // compile error
// In functions too
def sendEmail(to: Email, body: String :| MinLength[1]): Unit = ???
Iron's Strengths
- Type-safe without runtime validation — memory-compatible with plain
Int/String - Compile-time checks via Scala 3 macros — the compiler validates literals
- Define arbitrary conditions — encode domain rules in types
// User domain types
type UserId = Int :| Positive
type UserName = String :| (MinLength[2] & MaxLength[50])
case class User(id: UserId, name: UserName)
// Blocked at compile time
val u = User(0, "X")
// ERROR: 0 is not Positive, "X" is not MinLength[2]
Comparison With Other Languages
- TypeScript Brand types — a weaker version of Iron
- Rust's newtype pattern — similar intent, weaker validation
- Haskell refined types — the closest equivalent
- F# units of measure — partial overlap
Iron is one of the most powerful type-safety tools Scala has in 2026.
13. doobie / Slick — DB Libraries
Scala's DB libraries split mainly between doobie (functional) and Slick (SQL DSL).
doobie — Pure Functional JDBC
doobie wraps JDBC functionally on top of Cats Effect.
import doobie._
import doobie.implicits._
import cats.effect._
val xa = Transactor.fromDriverManager[IO](
driver = "org.postgresql.Driver",
url = "jdbc:postgresql:test",
user = "u",
password = "p",
logHandler = None
)
case class User(id: Int, name: String)
// SQL interpolation — type-checked at compile time
def findUser(id: Int): ConnectionIO[Option[User]] =
sql"SELECT id, name FROM users WHERE id = $id".query[User].option
val program: IO[Option[User]] = findUser(1).transact(xa)
Pros — SQL is SQL, type-safe, perfect Cats Effect integration. Cons — you must know SQL well, no ORM auto-mapping.
Slick — Type-Safe SQL DSL
Slick expresses SQL as a Scala DSL. It's not an ORM — closer to a query builder.
import slick.jdbc.PostgresProfile.api._
class Users(tag: Tag) extends Table[(Int, String)](tag, "USERS"):
def id = column[Int]("ID", O.PrimaryKey)
def name = column[String]("NAME")
def * = (id, name)
val users = TableQuery[Users]
// Write SQL in Scala
val query = users
.filter(_.id === 1)
.map(_.name)
.result
val action = query
db.run(action).map(println)
Pros — safe queries without knowing SQL, IDE auto-complete. Cons — DSL hits a wall on complex queries, debugging is hard.
Which to Pick
| Situation | Recommendation |
|---|---|
| Team knows SQL well | doobie |
| Migrating from ORM-comfortable team | Slick |
| Cats Effect stack | doobie (natural fit) |
| Complex dynamic queries | doobie (raw SQL freedom) |
| With automated migrations | Slick (strong codegen) |
Quill — Another Option
Quill compiles Scala code to SQL at compile time — the ZIO camp's favorite option.
import io.getquill._
val ctx = new SqlMirrorContext(PostgresDialect, SnakeCase)
import ctx._
case class User(id: Int, name: String)
val q = quote {
query[User].filter(_.id == 1).map(_.name)
}
println(ctx.run(q).string)
// SELECT name FROM user WHERE id = 1
14. Lichess — A Chess Site Written in Scala
One of the most famous production systems built in Scala is Lichess — a 100% free, 100% open-source chess site with millions of monthly active users.
The Scale of Lichess
| Metric | Value (2025) |
|---|---|
| Daily games | 7 million+ |
| Monthly active users | 100 million+ page views |
| Concurrent players | Tens of thousands |
| Staff | 3-4 full-time |
| Operating cost | ~$10,000/month (donation-funded) |
| Codebase | 100% Scala (backend) |
The efficiency is wild. While comparable sites employ dozens of engineers, Lichess runs on a small team and Scala.
Why Lichess Uses Scala (Summary of Thibault Duplessis Interviews)
- The type system catches bugs at compile time — essential when operations staff is small
- Functional paradigm keeps concurrency safe — handling tens of thousands of concurrent users safely
- Akka (now Pekko) handles games as actors — one actor per game
- MongoDB + reactivemongo for async DB — all Scala-friendly
Lichess's Tech Stack
- Language — Scala 3 (migration from Scala 2 completed in 2024)
- Web framework — Play Framework
- Actor model — Pekko (formerly Akka)
- DB — MongoDB
- Frontend — TypeScript (Snabbdom)
- Infra — bare-metal servers (OVH)
Takeaways
- Scala's expressiveness is decisive when a small team runs a big system
- The "startup = JS / Python" formula isn't absolute
- A model for open-source operations — all code is at github.com/lichess-org
Lichess is living proof for teams evaluating Scala that "this is possible."
15. Korea / Japan — Toss, MegaZone, Septeni, Money Forward
Scala isn't mainstream in Korea or Japan, but it's actively used at specific companies.
Scala Adoption in Korea
| Company | Use case | Notes |
|---|---|---|
| Toss Payments | Some backends (settlement, statistics) | Core is Kotlin, parts are Scala |
| MegaZone Cloud | Data platform | Spark-based |
| Parts of LINE | Data engineering | Spark-centric |
| Parts of Kakao | Search and recommendation systems | Spark + Scala |
| Many data companies | Spark-based ETL | Scala is the de facto standard |
In Korea, Scala is alive in the data-engineering and platform space, while pure backend has mostly moved to Kotlin/Java.
Scala Adoption in Japan
Japan adopts Scala more actively than Korea.
| Company | Use case | Notes |
|---|---|---|
| Septeni (CyberAgent group) | Ad-platform backends | Heavy Scala usage |
| Money Forward | Household finance and B2B accounting | Scala + Rails mix |
| freee | Parts of accounting SaaS | Some Scala modules |
| Chatwork | Chat SaaS | Scala + Pekko |
| CyberAgent (AbemaTV) | Parts of the video platform | Scala + Go |
| Dwango | Niconico Douga | History of Scala usage |
Japan has had an active Scala community since the mid-2010s, with conferences like ScalaMatsuri running every year.
Should You Learn Scala in East Asia
- Pure backend developer — Kotlin first, Scala optional
- Data engineer — Scala is a big weapon if you go deep on Spark
- Want deep FP — Scala is the best FP language on the JVM
- Job market — narrow in Korea, present and centered in Tokyo for Japan
Learning Resources (Korean / Japanese)
- Scala School (Twitter, Korean translation)
- Programming in Scala (Odersky, Korean translation)
- Japanese — Scala逆引きレシピ
- ScalaMatsuri — Tokyo, annual
16. Who Should Pick Scala — Data / FP / JVM + ML
An honest take on who should learn Scala in 2026 and who shouldn't.
Scala Fits If
- Data engineer — environments centered on Spark
- Deep FP — drawn to Haskell or OCaml but need the JVM ecosystem
- High-performance concurrency — actor-based systems with Pekko
- Type-safety frontier — Iron, refined types, capture checking
- Small team running a big system — Lichess-level operational efficiency
- ML on the JVM — BigDL, Smile, Frameless
Scala Does Not Fit If
- You need fast hiring — Scala engineers are relatively rare
- Simple CRUD backend — Kotlin / Spring delivers much faster
- You just joined a Java/Kotlin team — political cost is high
- Solo full-stack development — the JS / TS ecosystem is richer
- Cloud Native and Kubernetes tooling — Go is the de facto standard
Suggested Learning Path
Beginner
↓
Small scripts with Scala CLI (1-2 weeks)
↓
"Programming in Scala 5th Edition" or
"Effective Programming in Scala" (Scala Center)
↓
Get comfortable with case class / sealed trait / for comprehension
↓
Pick a camp: ZIO or Cats Effect
↓
Real project — a small API with http4s + Tapir
↓
Expand to Pekko, Spark, Iron, doobie as needed
Position Against Other JVM Languages
| Language | Position | Relationship to Scala |
|---|---|---|
| Java | Rich, many companies | Scala complements — use Scala only where Java struggles |
| Kotlin | The new backend strong man | Lighter than Scala, weaker FP |
| Clojure | Lisp family, dynamic | Very different philosophy from Scala |
| Groovy | DSL niche | Scala is more powerful |
ML on the JVM
Python is the ML standard, but the JVM camp has:
| Library | Role |
|---|---|
| BigDL | Distributed deep learning on Spark |
| Smile | Statistics and ML library |
| Frameless | Type-safe Spark |
| Deeplearning4j | DL on the JVM (Java-leaning) |
| ONNX Runtime Java | Train in Python, infer on the JVM |
The "train in Python, infer on the JVM" pattern is growing, and Scala handles that inference layer cleanly.
17. References
Scala Official
Build Tools
Effect Systems
Actors and Concurrency
Web Frameworks
Other Platforms
Data
Type System
DB
Testing and Logging
Effect.ts (Scala Influence)
Notable Production Cases
Conferences and Community
Books
- "Programming in Scala, 5th Edition" — Martin Odersky et al.
- "Effective Programming in Scala" — Julien Richard-Foy (Scala Center)
- "Functional Programming in Scala, 2nd Edition" — Paul Chiusano, Runar Bjarnason
- "Zionomicon" — John A. De Goes, Adam Fraser (ZIO book, free)