Language Choice on the JVM

Marcus • January 31, 2021

Compass on a map of Prague’s old town

The Java Virtual Machine is providing the groundwork for a number of languages hosted on it: There’s Groovy, Scala, Clojure, and quite a few more.

Which particular programming language a company or team decides to use tends to be constrained by a variety of factors, but perhaps here’s the most common ones I would personally consider.

Building from the foundation up

A key component both when making any particular language choice is your teams’ familiarity with the language itself.

The first developer or the first team will likely have a preference on their own already. Matched with the product vision and opinions of prospective customers and potential stakeholders, there’s a strong early influence in the design that, while it may later prove wrong, shapes how your software is fundamentally built.

Does your software run in the cloud, where you would often find web frontends, or on-premises, giving more notion to classic user interfaces[1]? For more traditional user interfaces, finding a framework with a native look & feel isn’t a trivial exercise, and that alone can often be the deciding factor to pick a non-Java UI together with a Java backend.

At the very least, you can reasonably expect developers of software that runs on the JVM to be familiar with Java itself – if only to understand runtime constraints: If there’s no reified generics, language support implies additional complexity.

As people join your company, you want to get them up to speed quickly. Similarly, team compositions change throughout the years: Whoever leaves, of course, should never leave a vacuum of knowledge behind where further development and maintenance becomes a struggle.

Your software needs to be understandable to everyone who is working on it. This typically resembles a compromise over what can be established as a shared base of knowledge. While people come and go over the course of many years, you want new developers to get up to speed quickly.

What you will eventually settle on is up to team and company preference – if all languages are executable within the JVM, there’s a wide array of choices. And if you could use any language to pick for code to run, given that as a sole constraint, it is perhaps reasonable to arrive at any number of languages.

If your particular type of application would be vastly simplified to read and under one language, then that’s a good reason in and of itself. Actively supported development environments typically support a variety of JVM flavors; and it’s in every language’s best interest to make the transition as easy as can be for a Java developer.

Kotlin

There are three (perhaps 3.5) points that the official Kotlin site outlines as reasons to use Kotlin:

Groovy

Outside of university, I’ve not looked at Groovy much, admittedly: It’s a dynamic language, which – yes, can work very well (and I’ve worked enough with Ruby, Python, … in the past), but I personally find less appealing at scale: I’m not under the impression that, at least in my current company, we’d ship software that would suddenly work.

Developer tooling often supports extensibility: Gradle scripts and Jenkins build scripts are two places where I often use Groovy; and while that may not be enough to justify putting Groovy experience on my résumé, but knowing a few language fundamentals has certainly proven useful.

Common Traits

Both Groovy and Kotlin implement a few things that seem entirely useful to writing and reading code, where Java can feel ancient in the developer experience.

Self-Imposed Constraints

I do personally find the more stringently, statically-typed approach of Kotlin to be more approachable: It’s perhaps limiting that it’s a bit less dynamic and doesn’t support true metaprogramming – Groovy does, but using every trick in the box is rarely a good idea.

Magic you only find accidentally (or, more commonly, if things fall apart) is only useful in rare cases – and needs to be called out very explicitly in the documentation. I’ve seen rough spots around self-developed technical frameworks that officially make things easier: The wiring is sometimes unnecessarily arcane where simplicity would have sufficed.

It is anyone best guess why our framework defines save() on each entity rather than on the data access layer, and why it more critically is a no-op unless you jumped through some hoops before calling it. You can of course learn all the intricacies of said system, but – and this is what we are currently doing – you might just want to lift new developments to a simpler environment.


  1. Of course, this distinction has lessened over time with new technologies such as Electron, but may only have a marginal impact for companies that have existed for a few years longer than the relevant technology has.
  2. Interestingly enough, the x to y syntax isn’t limited to maps in Kotlin either: You create a Pair, which only in this particular instance will be inserted into a map.
  3. Kotlin resolves extension functions statically and not at compile time, hence making them easier to grasp in my humble opinion.
}