Skip to content(if available)orjump to list(if available)

Scala 3 slowed us down?

Scala 3 slowed us down?

113 comments

·December 7, 2025

game_the0ry

I am not a scala fan and do not care for it, but I upvote for the thorough thought process, breakdown, and debugging of the problem. This is how technical blogs should be written. AI aint got shit on this.

sema4hacker

> I was refreshing one of our services. Part of this process was to migrate codebase from Scala 2.13 to Scala 3.

My first question was: why?

pxc

Scala 3 is sorta a new language, bringing a lot of improvements to the type system: https://docs.scala-lang.org/scala3/new-in-scala3.html

It also looks like it has some improvements for dealing with `null` from Java code. (When I last used it I rarely had to deal with null (mostly dealt with Nil, None, Nothing, and Unit) but I guess NPEs are still possible and the new system can help catch them.)

_old_dude_

In Scala 3, the inline keyword is part of the macro system.

When inline is used on a parameter, it instructs the compiler to inline the expression at the call site. If the expression is substantial, this creates considerable work for the JIT compiler.

Requesting inlining at the compiler level (as opposed to letting the JIT handle it) is risky unless you can guarantee that a later compiler phase will simplify the inlined code.

There's an important behavioral difference between Scala 2 and 3: in 2, @inline was merely a suggestion to the compiler, whereas in 3, the compiler unconditionally applies the inline keyword. Consequently, directly replacing @inline with inline when migrating from 2 to 3 is a mistake.

AdieuToLogic

> There's an important behavioral difference between Scala 2 and 3: in 2, @inline was merely a suggestion to the compiler, whereas in 3, the compiler unconditionally applies the inline keyword. Consequently, directly replacing @inline with inline when migrating from 2 to 3 is a mistake.

This reminds me of a similar lesson C/C++ compilers had to learn with the "auto" keyword. Early versions treated the keyword as a mandate. As compiler optimizers became more refined, "auto" was first a recommendation and then ultimately ignored.

The C++ inline keyword is treated similarly as well, with different metrics used of course.

dtech

Kotlin heavily uses the inline keyword basically everywhere, to get rid of lamdba overhead for functions like map. Basically every stdlib and 3rd part library function that takes a lamdba is inlined.

In general it's a performance benefit and I never heard of performance problems like this. I wonder if combined with Scala's infamous macro system and libraries like quicklens it can generate huge expressions which create this problem.

pjmlp

This is one example why being a guest language isn't optimal.

They should have made use of JVM bytecodes that allow to optimize lambdas away and make JIT aware of them, via invokedynamic and MethodHandle optimizations.

Naturally they cannot rely on them being there, because Kotlin also needs to target ART, JS runtimes, WebAssembly and its own native version.

dtech

Kotlin existed before Java 7 and kept support JVM 1.6 for a long time (mainly because of Android)

Even then, they benchmarked it, and inlining was still faster* than invokedynamic and friends, so they aren't changing it now JVM 1.8+ is a requirement.

* at the expense of expanded bytecode size

gavinray

There are Kotlin compiler flags to default to "indy" optimization, and which may be enabled by default for some time now?

Also not all Kotlin inlines are lambdas or even include method calls

gavinray

The killer is specifically the inlining of macros -- which Kotlin lacks.

And not all macros, but just the ones which expand to massive expressions

Think template expressions in C++ or proc macros in Rust

hunterpayne

The problem with Scala 3 is that nobody asked for it. The problem with Scala 2 is that the type inference part of the compiler is still broken. Nobody worked on that. Instead they changed the language in ways that don't address complaints. Completely ignore the market and deliver a product nobody wants. That's what happened here.

PS Perhaps they should make an actual unit test suite for their compiler. Instead they have a couple of dozen tests and have to guess if their compiler PR will break things.

lispisok

I tried getting into Scala several times and kept going back to Clojure. Unless you are into type system minigames Clojure has many of the things Scala advertises but without the dumptruck of Scala overhead and complexity. Another commenter briefly touched on this but it's a language made by academics for academics to play with language design. It was a little weird it blew up in industry for a while.

acjohnson55

> it's a language made by academics for academics to play with language design. It was a little weird it blew up in industry for a while.

Yep. They have always been pretty honest about this.

I think that it blew up in industry because it really was ahead of its time. Type systems were pretty uncool before Scala. It proved that you could get OO and FP in a single type system.

Actually, a big part of reason for doing Scala 3 was rebasing the language on a more rigorous basis for unifying OO and FP. They felt that for all their other big ideas, it was time to rethink the fundamentals.

refulgentis

> Type systems were pretty uncool before Scala

I’m not up on programming language engineering as much as I should be at 37, could you elaborate a bit here? (To my untrained ear, it sounds like you’re saying Scala was one of the first languages that helped types break through? And I’m thinking that means, like, have int x = 42; or Foo y = new Foo()”

thefaux

It's sad but I generally agree. Scala was in my view pretty well positioned for an up and coming language ~2010-15. Not only did the scala 3 rewrite fail to address many of the most common pain points -- compile times and tooling immediately come to mind -- the rewrite took many years and completely stalled the momentum of the project. I have to wonder at this point who is actually starting a new project in scala in 2025.

It's really a shame because in many ways I do think it is a better language than anything else that is widely used in industry but it seems the world has moved on.

zahlman

>It's sad but I generally agree. Scala was in my view pretty well positioned for an up and coming language ~2010-15

I used Scala for a bit around that period. My main recollection of it is getting Java compiler errors because Scala constructs were being implemented with deeply nested inner classes and the generated symbol names were too long.

voidfunc

Scala has deep roots in the Ivory Towers of Academia, its not shocking they think they know better than their users what the problems with the language are and didn't do any kind of real product management to figure out the actual problems before embarking on a rebuild.

oelang

And I wish you read the article, you're comments are completely off topic.

lmm

> The problem with Scala 2 is that the type inference part of the compiler is still broken. Nobody worked on that. Instead they changed the language in ways that don't address complaints.

Huh? Type inference is much more consistent and well-specified in 3. In 2 it was ad-hoc so and impossible to fix anything for one codebase without breaking another. There are plenty of legitimate complaints to be had about Scala 3, but this is absolutely not one of them.

dmix

> After upgrading the library, performance and CPU characteristics on Scala 3 became indistinguishable from Scala 2.13.

We had a similar experience moving Ruby 2->3, which has a ton of performance improvements. It was in fact faster in many ways but we had issues with RAM spiking in production where it didn't in the past. It turned out simply upgrading a couple old dependencies (gems) to latest versions fixed most of the issues as people spotted similar issues as OP.

It's never good enough just to get it running with old code/dependencies, always lots of small things that can turn into bigger issues. You'll always be upgrading the system, not just the language.

jiehong

> After upgrading the library, performance and CPU characteristics on Scala 3 became indistinguishable from Scala 2.13.

Checking the bug mentioned, it was fixed in 2022.

So, I’m wondering how one would upgrade to scala 3, while keeping old version of libraries?

Keeping updated libraries is a good practice (even mandatory if you get audits like PCI-DSS).

That part puzzled me more than the rest.

tasuki

> Keeping updated libraries is a good practice

First, the "good practice" argument is just an attempt to shut down the discussion. God wanted it so.

Second, I rather keep my dependencies outdated. New features, new bugs. Why update, unless there's a specific reason to do so? By upgrading, you're opening yourself up to:

- Accidental new bugs that didn't have the time to be spotted yet.

- Subtly different runtime characteristics (see the original post).

- Maintainer going rogue or the dependency getting hijacked and introducing security issues, unless you audit the full code whenever upgrading (which you don't).

mystifyingpoi

I'm confused as well, because he wrote

> I did it as usual - updating dependencies

but later

> After upgrading the library, performance and CPU characteristics on Scala 3 became indistinguishable from Scala 2.13.

So... he didn't upgrade everything at first? Which IMO makes sense, generally you'd want to upgrade as little as possible with small steps. He just got unlucky.

gavinray

It would have been a transitive dependency based on the comments about the library being "transparent" and the author unaware it was even used.

Pinning specific versions of transitive deps is fairly common in large JVM projects due to either security reasons or ABI compatibility or bugs

fn-mote

> Checking the bug mentioned, it was fixed in 2022.

I was considerably less impressed by the reporting when I finally found out the culprit.

Sure it was “Scala 3” … but not really.

It was an interaction of factors and I don’t think it would take away from the story to acknowledge that up front.

lmm

> So, I’m wondering how one would upgrade to scala 3, while keeping old version of libraries?

The normal way.

> Keeping updated libraries is a good practice

So is changing one thing at a time, especially when it's a major change like a language version upgrade.

gavinray

If your Maven/Gradle/SBT build specifies a version constraint for a third party lib, updating your Scala or Kotlin version doesn't affect this

(For scala-specific libs, there is a bit more nuance, because lib versions contain scala version + lib version, e.g. foolib:2.12_1.0.2 where 2.12 = scala version)

spockz

For me the main takeaway of this is that you want to have automated performance tests in place combined with insights into flamegraphs by default. And especially for these kind of major language upgrade changes.

malkia

Benchmarking requires a bit of different setup than the rest of the testing, especially if you want down to the ms timings.

We have continous benchmarking of one of our tools, it's written in C++, and to get "same" results everytime we launch it on the same machine. This is far from ideal, but otherwise there be either noisy neighbours, pesky host (if it's vm), etc. etc.

One idea that we thought was what if we can run the same test on the same machine several times, and check older/newer code (or ideally through switches), and this could work for some codepaths, but not for really continous checkins.

Just wondering what folks do. I can assume what, but there is always something hidden, not well known.

spockz

I agree for measuring latency differences you want similar setups. However, by running two versions of the app concurrently on the same machine they both get impacted more or less the same by noisy neighbours. Moreover, by inspecting the flamegraph you can, manually, see these large shifts of time allocation quickly. For automatic comparison you can of course use the raw data.

In addition you can look at total cpu seconds used, memory allocation on kernel level, and specifically for the jvm at the GC metrics and allocation rate. If these numbers change significantly then you know you need to have a look.

We do run this benchmark comparison in most nightly builds and find regressions this way.

malkia

Good points there - Thanks @spockz!

esafak

What are folks using for perf testing on JVM these days?

cogman10

For production systems I use flight recordings (jfrs). To analyze I use java mission control.

For OOME problems I use a heap dump and eclipse memory analysis tool.

For microbenchmarks, I use JMH. But I tend to try and avoid doing those.

spockz

I use jmh for micro benchmarks on any code we know is sensitive and to highlight performance differences between different implementations. (Usually keep them around but not run on CI as an archive of what we tried.)

Then we do benchmarking of the whole Java app in the container running async-profiler into pyroscope. We created a test harness for this that spins up and mocks any dependencies based on api subscription data and contracts and simulates performance.

This whole mechanism is generalised and only requires teams that create individual apps to work with contract driven testing for the test harness to function. During and after a benchmark we also verify whether other non functionals still work as required, i.e. whether tracing is still linked to the right requests etc. This works for almost any language that we use.

noelwelsh

jmh is what I've always used for small benchmarks.

gavinray

async-profiler

derriz

I was involved in a Scala point version migration (2.x) migration a few years ago. I remember it being painful. Although I recall most of the pain was around having lots of dependencies and waiting for libraries to become available.

At the time Scala was on upswing because it had Spark as its killer app. It would have been a good time for the Scala maintainers to switch modes - from using Scala as a testbed for interesting programming-language theories and extensions to providing a usable platform as a general commercially usable programming language.

It missed the boat I feel. The window has passed (Spark moved to Python and Kotlin took over as the "modern" JVM language) and Scala is back to being an academic curiosity. But maybe the language curators never saw expanding mainstream usage as a goal.

hylaride

Outside of Android work, has Kotlin really taken over? My understanding is that Java added a lot of functional programming and that took a lot of wind out of Scala's sails (though Scala's poor tooling certainly never helped anything).

mystifyingpoi

> My understanding is that Java added a lot of functional programming

This is true, but needs more context. Java 8 added Stream API, which (at this time) was a fantastic breath of fresh air. However, the whole thing felt overengineered at many points, aka - it made complex things possible (collector chaining is admittedly cool, parallel streams are useful for quick-and-dirty data processing), but simple everyday things cumbersome. I cannot emphasize how tiring it was to have to write this useless bolierplate

  customers.stream().map(c -> c.getName()).collect(Collectors.joining(", "))
for 1000th time, knowing that

  customers.map(c -> c.getName()).join(", ")
is what users need 99.99999% of the time.

dtech

Sort of true, but I often hear this take from Java programmers and it feels like "Blub" [1]/Stockholm syndrome to me.

Personally, I'm extremely glad to not have had to write .toStream().map(...).collect(Collectors.list()) or whatever in years for what could be a map. Similar with async code and exception handling.

For me one of the main advantages of Kotlin is that is decreases verbosity so much that the interesting business logic is actually much easier to follow. Even if you disregard all the things it has Java doesn't the syntax is just so much better.

[1] https://paulgraham.com/avg.html

gavinray

My org had to write a pivotal backend service on the JVM, due to JDBC having the largest number of data source adapters.

The choice was Kotlin. Scala is too "powerful" and can be written in a style that is difficult for others, and Java too verbose.

Kotlin is instantly familiar to modern TypeScript/Swift/Rust etc devs.

The only negative in my mind has been IntelliJ being the only decent IDE, but even this has changed recently with Jetbrains releasing `kotlin-lsp` for VS Code

https://github.com/Kotlin/kotlin-lsp

aarroyoc

At least where I work, writing new Java code is discouraged and you should instead use Kotlin for backend services. Spring Boot which is the framework we use, supports Kotlin just fine, at the same level as Java. And if you use Jetbrains tools, Kotlin tooling is also pretty good (outside Jetbrains I will admit it is worse than Java). Now, even in new Java projects you can still be using Kotlin because it is the default language for Gradle (previously it was Groovy).

kelnos

Java did indeed add more FP to the language, but Java's type system is still fairly primitive compared to Scala's.

esafak

Java's new features are always going to be on paper. The ecosystem, with all its legacy code, is always going to be a decade behind. And if you are starting a new project, why would you pick Java over Kotlin?

frje1400

> And if you are starting a new project, why would you pick Java over Kotlin?

Because in 5-10 years you'll have a Java project that people can still maintain as if it's any other Java project. If you pick Kotlin, that might at that point no longer be a popular language in whatever niche you are in. What used to be the cool Kotlin project is now seen as a burden. See: Groovy, Clojure, Scala. Of course, I recognize that not all projects work on these kinds of timelines, but many do, including most things that I work on.

pjmlp

Because the Java Virtual Machine is designed for Java, and that is what all vendors care about.

Kotlin is Google's C#, with Android being Google's .NET, after Google being sued by coming up with Google's J++, Android Java dialect.

Since Google wasn't able to come up with a replacement themselves, Fuchsia/Dart lost the internal politics, they adopted the language of the JetBrains, thanks to internal JetBrains advocates.

hylaride

That's kind of what I'm asking. I did have a former co-worker write a micro service in Kotlin around 2018. He said that as nice as the language is, the ecosystem was (at the time, not sure how it is today) so utterly dominated by Android development, that he said he wouldn't recommend using it again - half the time he was calling out Java anyways.

adrianN

It’s a lot cheaper to hire for Java than for „modern“ languages.

wrathofmonads

Kotlin hasn’t made much of an impact in server-side development on the JVM. I’m not sure where this perception comes from, but in my experience, it’s virtually nonexistent in the local job market.

izacus

Why is your personal experience relevant to the wider market? How many companies and locations did you survey for that?

strobe

another issue with kotlin, because it encourage Java ecosystem usage like Spring is not much differentiation that could drive adoption.

pjmlp

Kotlin is an Android language, because Google says so, and they stiffle Java support on purpose (Java 17 LTS subset currently).

Outside Android, I don't even care it exists.

If I remember correctly, latest InfoQ survey had it about 10% market share of JVM projects.

pjmlp

The only issue I have with Scala 3 is Python envy, they should not have come up with a second syntax, and pushing it as the future.

If anything is slowly down Scala 3 is that, including the tooling ecosystem that needs to be updated to deal with it.

noelwelsh

Everything is up to date with the new syntax as far as I'm aware. Also, the compiler and scalafmt can rewrite one to the other. A project can pick whatever style it wants and have CI reformat code to that style.

lmm

> Everything is up to date with the new syntax as far as I'm aware.

The Eclipse plugin isn't, and none of the newer IDE integrations is reliable.

pjmlp

When I checked a year ago, the IDE tooling still wasn't quite there.

blandflakes

I always find downvoting on stuff like this perplexing. It still isn't there. I know that a lot of Scala people are doing metals and some kind of text editor experience, but if you've used something as powerful as Intellij, the Scala 3 experience is a serious downgrade, and it still is today, even though it's better than it was a year ago.

spockz

What I don’t get because there is LSP and BSP support. What else is needed to get support for scala 3 from an IDE? Obviously, Kotlin coming from Jetbrains will make it receive a lot more love and first class support.

esafak

You could also have compared it, more attractively, to Haskell.

pjmlp

Except the reason behind the syntax change is the losing mindshare from Scala into Python, after its relevance in the big data wave that predated the current AI wave.

Nothing to do with Haskell, even if it is also white space significant.

gedy

As a former Scala fan, wow you aren't kidding, wth

    val month = i match
        case 1  => "January"
        case 2  => "February"
        // more months here ...
        case 11 => "November"
        case 12 => "December"
        case _  => "Invalid month"  // the default, catch-all
    
    // used for a side effect:
    i match
        case 1 | 3 | 5 | 7 | 9  => println("odd")
        case 2 | 4 | 6 | 8 | 10 => println("even")
    
    // a function written with 'match':
    def isTrueInPerl(a: Matchable): Boolean = a match
        case false | 0 | "" => false
        case _ => true

jfim

It's been a while since I touched Scala but wasn't that a thing in previous versions, minus the braces not being present?

weego

Yes, that's all just as it was, and in places braces were not required / interchangeable so this is more of an optional compiler choice than a real change

malkia

Sorry, I'm coming from C++-ish background - can anyone explain what's going on :)

hocuspocus

Scala 2's syntax is mostly Java/C-style with a few peculiarities.

Scala 3's optionally allows indentation based, brace-less syntax. Much closer to the ML family or Python, depending on how you look at it. It does indeed look better, but brings its share of issues.[1] Worse, a lot of people in the community, whether they like it or not, think this was an unnecessary distraction on top of the challenges for the entire ecosystem (libraries, tooling, ...) after Scala 3.0 was released.

- [1] https://alexn.org/blog/2025/10/26/scala-3-no-indent/

bdangubic

madness :)

a24j

Can you eli5 the madness? And how that relates to python/java?

xolve

The bug reports linked on softwaremill and scala GitHub's are precise and surprisingly small fixes! It does show Scala's power in expressiveness.

Scala is a great language and I really prefer its typesafe and easy way to write powerful programs: https://www.lihaoyi.com/post/comlihaoyiScalaExecutablePseudo... Its a great Python replacement, especially if your project is not tied to ML libraries where Python is defacto, like JS on web.

rr808

I'm on Spark Scala 2 project and I hate it. Basically any good Scala dev would never want to work on our ETL projects, so we get second rate Python or Java devs like me who bastardize the language to get anything to work. Most of our new stuff is all pyspark, hopefully we can replace Scala asap.

Kwpolska

The takeaway of upgrading your libraries when upgrading major language and framework versions applies beyond Scala. Especially when the libraries abuse magic language features (and far too many Scala libraries do) or otherwise integrate deep into the framework/language.