Java LTS - perspective of a library author

5 minute read

I have had intuitively mixed feelings with the proposed 2 years Long Term Support plan of OpenJDK (from 3 today) so I wanted to write down what was causing it. These do not represent my employer’s view, more my perspective having spent 17 years leading, maintaining and contributing to various Java frameworks, application stacks and tools. I wrote this blog entry in one quick block of time so these ideas are not necessarily fully developped. I hope they can contribute to the debate on the subject - I’m happy to change my mind.

More is better, right?

On the surface it’s a great idea. New features are becoming mainstream faster (mean time between LTS reduced by a third). And which customer in its right mind would say no to more features faster?

That view to me does not take into account the ecosystem. Java is a VM, a platform really, a few languages, a ton of libraries and a gigaton of apps.

As an end user, you want the latest and greatest when you start a project and then you will fight tooth and nail to not have to upgrade. We all get it, upgrading is best, but most shops are not set for this. Heck even the biggest cloud providers on the planet took a long time to support Java 11 on their platform.

Libraries between a rock and hard place

This creates a demand distorsion for library maintainers that need to support old versions of the JVM, while allowing an app to use the latest versions of the JVM. Old versions means 8 or 11 depending on appetite.

It is roughly doable for a library to do this but requires more and more hacks as time pass before you reset to a new base version. And then there are catastrophic events of two kinds: the big new feature kind and the breaking kind.

New features are things like annotations, lambda, JFR custom events. They are awesome but might require the library to be rethought and impact its API surface. This makes it non compatible with the previous version of the library. No-one likes to be abandoned by libraries and library writers tend not to like to abandon their users. This is I think a characteristic of the Java ecosystem that is at the core of its longevity and there are plenty of counter examples in other communities of epic failure due to these breakages being too often or too big. My core hypothesis is that you can do a certain amount of breakage but not too many and the current Java LTS of 3 years does have that balance. To be fair, the versions people effectively jump to in mass are a subset of the LTS. It depends on the mix of good new features and libraries also embracing it (more on that later).

Then there are things that the JVM breaks or plans to break. Note that a lot of it is related to what the JVM designers felt were internal contracts and thus breakable. But library writers have used some of these internal contracts to make Java a super awesome platform. Both are right, both are wrong. Unsafe, modules being non open and requiring explicit list of open classes, the Security Manager likely future removal, the coming of Loom, all of these are for some libraries strong breaking frontiers that do not allow a library to support both the old JVM version and the new one.

A complex system, not a mere complicated one

Now here is the reason why this system is complex and not merely complicated. There is a tight chain of layered dependencies we could classify as the following (it’s a bad classification, but I want to make my point with an overly simple model):

  • App platforms like SpringBoot, Quarkus, Micronaut, WildFly, etc.
  • High level libraries or APIs used or exposed by these app platforms: Hibernate, Vert.x, EasyMock, Jakarta EE and Microprofile libraries, Kubernetes SDK, etc.
  • Low level libs that are used by the libs or above: Netty, ByteBuddy, OKHTTP, etc.
  • JVM (ab)used constructs like Unsafe, module visibility, Security Manager, Loom

So the ecosystem as a whole is trying to find the right time to abandon a given JVM version and be OK to break things. An example of this is when should libraries move a newer version of the Java EE now Jakarta EE version. A second example is OKHTTP that broke APIs (and added Kotlin as a dependency) and that split its ecosystem in two. The consequence is that user apps are caught between libraries upgrades with incompatible common dependencies. This is a library maintainer main fear: fragmenting its ecosystem or seeing a dependency fragmenting the ecosystem for it. It’s like a Mexican standoff and like in Reservoir Dogs, when one starts shooting, it has massive ripple effects.

Spring has announced that their next version will Java 17+ but I imagine they will be supporting 5.x for a looooong time and I’m sure they will hear from users that they want new features and dependency upgrades on old versions. We had a similar experience with Quarkus. While it is mainstream now, it was not at that stage yet at the time where we started talking about abandoning Java 8. You would not believe the massive amount of push back with good arguments from users and customers alike for not moving so fast. For Quarkus! Which was attracting early adopters willing to be more on the bleeding edge at the time!

More changes in a chaotic system

That leads me to my core argument. Touching the input parameters of a non linear complex system like the JVM ecosystem has wide ranging chaotic consequences. I do not think today that accelerating to a 2 year LTS cadence is worth it compared to the accelerated rhythm of chaos and cascading effects that will ensue to the entire ecosystem. The industry has not yet absorbed the Java 17 effect and won’t for quite a while.

My 2c. I hope this will help the conversations on the subject.