I feel like I should preface this with a disclaimer. My first job developing Java software was in 1998, and it has been a constant in my professional life since then; I was working with Enterprise Java Beans before anyone even knew what an EJB was, with the IBM “San Francisco” project that gave birth to that monster (and, for the record, I thought the morass of configuration-instead-of-code XML being created then was a mistake, but that’s another story), and in general I’ve been a big fan of the language and its ecosystem.
But… I can’t remember the last time I wrote a line of Java code. And I can’t think of a project I would start today using Java, either.
What brings me to this reminiscence is a good medium article by Jan Kammerath entitled We don’t need Java anymore – it belongs to a different era. Jan makes a lot of good points, and I can’t disagree with them; but I think he does miss one other nail in the coffin…
That nail is Containers. Or perhaps more importantly, containers and Orchestration. Deploying server-side applications - the one area that Java remains strong in - in containers and managing them with an orchestration framework like Kubernetes isn’t “the hot new thing” any more, it is the default for any new server side application - and Java just doesn’t play nicely in that world. Once again, the problem is the JVM and the memory management that Java became beloved for.
The JVM likes to give itself a nice big wedge of memory and then, well, manage
it. Which is great when the JVM is the only thing running on a (virtual)
machine - when the JVM is the only thing managing resources on a box, as the
EJB world envisioned, this works well. But when the JVM is just one of dozens
each running in their own “loosely isolated” container, they do not play
together nicely at all. At worst, you have entirely unpredictable
performance of the platform as a whole, at best you have chronically
inefficient use of your platform resources as everything gets wrapped in
request == limit. Sure, you can spend a lifetime tuning
your JVM parameters, but don’t try and convince me that is effort that
wouldn’t be better spent doing something more productive.
But if that is the tip of the nail in the JVM’s coffin, it’s the JVM’s startup time that gives it the shaft. The JVM starts up achingly slowly. “Well who cares about that?” say the EJB evangelists from 20 years ago? It’s not like you start up a new Tomcat instance every couple of minutes… But in the modern world of containers and orchestration, that’s exactly what we do. One of the key ways that Kubernetes et al. allow us to make more efficient use of resources is - through proper selection of metrics and scaling strategies - by letting the overall system find its own equilibrium by scaling up and down individual microservices in response to demand. Ideally, we should even be able to scale to zero - i.e. not run any instances of a service at all until it is needed, using tools like KNative - but this only works if we can spin up a functional instance of a service instantly when it is required, not 500ms later when a JVM has finished allocating all the memory on the server and spluttered into existence…
Now, of course, the world of Java recognises this problem - and it has solutions like GraalVM to solve them. I’ve been to several presentations by GraalVM sages, and I admire greatly what they are doing and their enthusiasm for it, but it has a problem… In order to actually get GraalVM to achieve the promise it holds, any application that is not trivial effectively requires a rewrite to work within the limitations that precompilation unavoidably imposes on a language that was never designed for it. And once you’re committed to rewriting your application… Well, why do it in Java?
So, I can’t help agreeing with Jan - Java, I’ve loved you so, but this is not
your era. Personally, I love Rust, it’s fantastic for containerised
workloads (you just can’t beat a Rust binary compiled for MUSL in a
container), but that’s not the only solution; many of our latest projects
are written in Golang which provides a nice balance between developer ease
and the light overhead needed for containerised workloads. But whatever the
language of the future is - it’s not Java, and it’s not anything that runs on