Skip to content
Published on

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

Authors

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

CurrentDetail
Scala 3 stabilizationScala 3.6 (Dec 2024) / 3.7 (Mar 2025) stable, Scala 4 preview in progress
Build tool diversificationsbt 2.x (slow transition), Mill (Li Haoyi), Scala CLI (scripts and POCs)
Effect system splitZIO 2 vs Cats Effect 3 — two camps coexist
Akka aftermathAkka moved to BSL (2022), Apache Pekko forked — now the de facto standard
Web framework maturityhttp4s, Tapir, Play Framework 3 — a functional web stack
Other platformsScala.js (JS), Scala Native 0.5 (native binaries)
Data engineeringApache Spark's core language — still a huge market
Idea exportsEffect.ts (TS), Iron (refined types), magnolify
AdoptionLichess, 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
  • -Wunused improvements — 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.

  • @experimental annotation 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:

TechniqueEffect
Incremental compilation (Zinc) improvementsOnly changed files recompile
Pickle cacheType info reuse
Parallel compilationModule-level parallelism
Using bloopBackground 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 given and using
  • 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
  • Task API 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

Itemsbt 2.xMillScala CLI
TargetLarge projectsMedium-largeScripts and small apps
Build definitionbuild.sbt (DSL)build.sc (Scala objects)using directives
Learning curveSteepMediumAlmost none
SpeedAverageFastVery fast
Plugin ecosystemVery richAverageLimited
Recommended forExisting sbt projectsNew projectsLearning 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 run
  • E (Error) — the error type the effect can fail with
  • A (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

  • Scope for resource managementZManaged was removed for a simpler API
  • ZStream stabilization — back-pressured stream processing
  • ZIO.attemptBlocking — automatic isolation of blocking work
  • ZIO.logSpan — distributed tracing integration

ZIO Ecosystem

LibraryRole
zio-httpHTTP server and client
zio-jsonJSON serialization
zio-configConfiguration
zio-quillCompile-time SQL generation
zio-kafkaKafka integration
zio-preludeFunctional data types
zio-schemaSchema-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

ItemZIO 2Cats Effect 3
Core typeZIO[R, E, A]IO[A]
Error trackingExplicit in the type (E)All Throwable
Dependency injectionLayer systemtypeclasses + Resource
Learning curveSteep (3 type parameters)Gentle (1 parameter)
Ecosystemzio-* seriesTypelevel camp
Philosophy"All together inside ZIO""Compose small libraries"

Cats Effect's Strengths

  • Typeclass-based — decomposed into MonadCancel, Concurrent, Async
  • Fiber for concurrency — lightweight green threads
  • Resource[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 Future or 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

SituationRecommendation
Full-stack web app (with templates)Play 3
Functional microservicehttp4s + Tapir
Large API + schema-firstTapir + any backend
Actor-based concurrencyPekko HTTP
New team, low learning overheadTapir + 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 infer let 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 Either and Option became 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

SituationRecommendation
Team knows SQL welldoobie
Migrating from ORM-comfortable teamSlick
Cats Effect stackdoobie (natural fit)
Complex dynamic queriesdoobie (raw SQL freedom)
With automated migrationsSlick (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

MetricValue (2025)
Daily games7 million+
Monthly active users100 million+ page views
Concurrent playersTens of thousands
Staff3-4 full-time
Operating cost~$10,000/month (donation-funded)
Codebase100% 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

CompanyUse caseNotes
Toss PaymentsSome backends (settlement, statistics)Core is Kotlin, parts are Scala
MegaZone CloudData platformSpark-based
Parts of LINEData engineeringSpark-centric
Parts of KakaoSearch and recommendation systemsSpark + Scala
Many data companiesSpark-based ETLScala 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.

CompanyUse caseNotes
Septeni (CyberAgent group)Ad-platform backendsHeavy Scala usage
Money ForwardHousehold finance and B2B accountingScala + Rails mix
freeeParts of accounting SaaSSome Scala modules
ChatworkChat SaaSScala + Pekko
CyberAgent (AbemaTV)Parts of the video platformScala + Go
DwangoNiconico DougaHistory 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)


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

LanguagePositionRelationship to Scala
JavaRich, many companiesScala complements — use Scala only where Java struggles
KotlinThe new backend strong manLighter than Scala, weaker FP
ClojureLisp family, dynamicVery different philosophy from Scala
GroovyDSL nicheScala is more powerful

ML on the JVM

Python is the ML standard, but the JVM camp has:

LibraryRole
BigDLDistributed deep learning on Spark
SmileStatistics and ML library
FramelessType-safe Spark
Deeplearning4jDL on the JVM (Java-leaning)
ONNX Runtime JavaTrain 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)