Write Once Run Everywhere, is it still that the way to go?
Write Once Run Everywhere has always been the motto for the Java language and it first selling point, but is it still that important? Let me share some thoughts on the topic.
The Java programming language was created by Sun Microsystems back in late '90s as a language that tried to solve some of the complexities of software development, while at the same time preserving a syntax that could still be familiar for users coming from other popular languages like C and C++.
One build to rule them all
The main advantage at the time was the possibility to build the application only once to generate the output JAR or WAR file, and then being able to run it wherever a Java Virtual Machine is available instead of having to create different build environments for each operating system that needs to be supported.
This of course was a huge advantage for software developers that could simplify their build environments while also getting compatibility with operating systems that were not initially thought to be supported.
This latter point in fact was the main idea of Sun Microsystems, that created Java with the main goal of getting more software compatible with its hardware in return, so that it could be more appealing to potential customers, because the main business for Sun was in fact selling the hardware, not the software.
In the last twenty years, Java has become one of the main languages for building enterprise applications, where compatibility is a huge selling point and one of the main goals for software developers. The creation of frameworks like Spring and more recently Spring Boot then helped simplify many aspect of the web and enterprise application development giving to Java another big push.
Things change
With the advent of cloud computing the software development landscape has completely changed.
What used to be run on a bare metal dedicated server inside the company's datacenter, with overprovisioned resources and often a single application per machine, now needs to be run in a container inside a Kubernetes cluster in a datacenter somewhere around the world.
All while consuming the least possible resources to improve instance utilization and reduce costs and why not, with the possibility to scale down to zero instances or up as high as traffic requires.
The shift is so huge that now Java is not always the best choice for the task. And even in the enteprise world its position as the de-facto choice is starting to become less obvious.
The new Cloud Native requirements
Java is a nice language, easy and clean, compiles fast and is supported by every library, tool an service available. So why could it not the best option for the cloud? Because of its legacy. Let me explain.
The motto "Write Once Run Everywhere" come at a cost, the Java Virtual Machine and its system abstraction.
The JVM is a really powerful tool and it works wonderfully if your goal is compatibility, but its main focus on "Run Everywhere" introduced a lot of abstractions between the operating system and the application. Java also introduced the Garbage Collector to simplify the life of developers, and both of these choices is a tradeoff between performance and ease of use.
Java applications can be quite fast once running for a while, in fact tools like Cassandra and Kafka are built in Java, but the Just In Time compiler and the JVM both take some time to "warm up". This means that the time required to start the application and to reach top performance is not that low, and can range from seconds to tens of seconds or more. Not a problem for an always on system, but not the best if you need to continously spin up and stop instances.
In addition to this the Garbage Collector, and the memory model of the language, require a lot of RAM during runtime. In fact it is quite common to find even simple applications that take several GigaBytes of RAM after running for a while.
Now that microservices, containerization and serverless systems are becoming the norm, these metrics of startup time and memory requirements make Java "too expensive" for the "cloud native" world.
The contenders
In recent years other languages and frameworks like JavaScript / TypeScript with the advent of Node.js and more recently of Deno, or Go and Rust are gaining more and more traction on the developers because they challenge the two main drawbacks of Java described before.
JavaScript and TypeScript are becoming ubiquitous and are the lingua franca of the frontend development, while Go powers the backbone of the major cloud infrastructure tools like Docker, Kubernetes, Prometheus, or even database systems like InfluxDB and CockroachDB and these just to name a few.
Rust is the "most loved programming language" on StackOverflow for the last five years in a row and is becoming more and more popular and used by major cloud providers to power the foundations of their infrastructures, and it is also becoming the first choice language when working with WebAssembly.
None of them is the perfect choice for everything, and there is no such thing like "the perfect programming language". Each option has its own pros and cons and is more suitable for one or more contexts or projects.
Javascript is a dynamically typed language and it is also interpreted, so its performance and memory usage is not the best, but it can still be very fast when used with modern VM like V8 or JavaScriptEngine (as Bun has recently demonstated).
Even Go has a Garbage Collector, but the way the language works is way more efficient that the Java counterpart, and by compiling to native binary, it doesn't require a virtual machine. If compiled statically it also has zero external dependencies.
Rust is a powerful and very nice language, has great performance and optimizations and it also compile to static binaries with zero external dependencies. But it has a steep learning curve and even if things are improving, it is quite slow to compile.
Even with their cons, these three options are still serious contenders and oftwn way better choices for cloud projects if compared to Java.
The new landscape
As we saw there are now a lot of options to choose from when starting a new project, even an enterprise one, and the "run everywhere" thing took a different path, because in the last two decades Linux has become the de-facto operating system on servers so there is not anymore that need to support every operating system available. In fact some of the tools named before only run on Linux systems.
This means that the new requirements for a cloud native applications are now:
- very fast startup time, so that they can be used in serverless or lambda platforms
- low memory usage, to spin up more instances of the application on the same servers
- (horizontal) scalability, to be able to react to spikes in traffic or to scale down to zero to reduce costs
- resilience, to be able to resist to crashes and attacks
In addition to thiese requirements, the new direction in cloud development, and not only for that, must be Zero Trust, because security of data and personal information should always be one of the main focus of software and systems design.
That said Java is not going anywhere anytime soon, but it will probably lose some market share in the years to come. The language is moving toward native compilation, faster startup times and lower memory usage, but the plans to reach all of these goals still take a few years. And in the meantime new languages and tools will have probably widen the gap with Java.
The next years will tell which language will be the most "cloud native", in the meantime let's try to use the best tool among the available ones.
I recently moved to Go after more than twelve years of Java only projects and after the introduction of generics in version 1.18. I have to say that I quite love it. As many say Go is "easy to learn but hard to master" so the future will tell if I made the right choice.