Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ


Choose your language

InfoQ Homepage Presentations Keeping Pace with Java

Keeping Pace with Java



Marc Hoffmann attempts to answer what are the important aspects of the new Java release schedule and what a pragmatic and sustainable update strategy looks like.


Marc Hoffmann is a Java Champion involved in various FOSS and commercial Java projects since the very beginning in 1996. In his spare time he created the free code coverage tool JaCoCo. This includes keeping up and testing with the latest Java releases. In his day job he is CTO of mtrail GmbH in Berne, Switzerland.

About the conference

QCon Plus is a virtual conference for senior software engineers and architects that covers the trends, best practices, and solutions leveraged by the world's most innovative software organizations.


Hoffmann: My name is Marc Hoffmann. I've been working with Java since the very first release, 1.0. I did Java projects with every release, from that time. For context, I would like to tell you what I'm doing today. This is what I do during the day. These systems, as you see a screenshot here, manage Railroad traffic. Railroad is actually a domain, which is more than 150 years old. This really sounds like legacy systems. Indeed, we develop and maintain such systems for more than 15 years. Actually, these systems have a real business case and provide real value. They do work. Probably, you're in a similar situation, that you cannot just pick the latest and shiniest software tools and features and develop a new project every other day. You work on a system which provides some value for your clients, for your company, and that needs to be maintained in the long run. I'm actually interested in that. How old is the software you're currently working on? What Java version are you currently using in production? This is an example for me. The software is about 15 years old. In production, we have Java 11.

What I Do At Night

I do more things. I call it what I do at night. I'm involved in some open source projects, probably the best known is the JaCoCo code coverage tool. With that tool, you can measure coverage provided by, for example, the unit tests. I create such reports. It's very popular in the QA area. It works on the latest Java versions, and this really looks like a nice technology in a nice playground to adapt to the latest technology and the latest features in the JVM.

Let's have a look at the reality of the new and shiny open source projects. The projects I'm working on are more than 10 years old. JaCoCo, I started 10 years ago. The code coverage Eclipse plugin, which was basically another tool before, is already 15 years old. These tools have seen quite a few Java releases. Actually, this is the big matrix for JaCoCo. Currently, we're supporting Java 5 up to Java 17. All these JDKs are actually used in our build pipeline. The integration tests are run on these pipelines. This is a situation when you develop a library.

Different Kinds of Software

There's basically two different kinds of software projects. You might work on a product or a project specific solution. Here, you just pick the runtime that fulfills your requirements best. You run a specific runtime version only. If you pride more on a tool or library, you have to support multiple runtimes, because you don't know your clients and your client might use your application with different runtimes. For context, I will focus on the first one, assuming you have a large application in production, and you want to regularly update them.

Why Update Your Application?

Here, the question is, first, why do you want to update your application? Second, how? Let's have a look at the why. With new Java versions, we get new useful language features and new APIs. This makes you more productive, and you want to use that to get a better code base, a cleaner code base, and to be more efficient in development of new features, and maintaining your code base. Second, there's a lot of bug fixes and security fixes going on. You want to have a maintained release of Java that you get these bug fixes and security fixes. There's new platforms. I have an example here, which is very popular currently, the ARM architecture, the new processors. You might know them from the Raspberry Pi you're playing with. It's a really nice platform to learn. Also, in production, we will see more, most notably, the recent release of the Apple M1 processors for the new laptops. We now have Windows 10 on ARM architecture. On Amazon, the A1 instance types are also based on ARM and getting more public because they provide a really nice ratio between power and price. They are really cost efficient. Probably, we will see more applications deployed on ARM architectures. We need new JVMs, new runtimes for that, that already support this architecture. You won't get that for Java 5.

We probably depend quite a lot on third-party libraries, and from time to time, we need to upgrade them. These libraries also have a minimum requirement of Java runtime. We need to update our runtime from time to time, then we can also update the libraries. Currently, I did some research on the Maven Central, most of the latest releases of libraries have Java 8 as a baseline. No panic yet, Java 8 has been around for more than eight years. There's a good point to be at least at Java 8, and Java libraries do not quickly add up to Java 11, whatever. They run the runtimes, but you're not required to update to Java 11 immediately. Be aware, most of the libraries are now on Java 8. If you're older than Java 8, you might get in trouble with updating them. Of course, we want to be happy developers, and you want to learn and work with the latest stuff. It's a good motivation to have more than a runtime in your development environment in production.

How to Update

Let's have a look at the main part, how do we update? Here, this is the release schedule in the old days of Java. What we see here is a major release every couple of years, like two years. Here was a huge gap between Java 6 and Java 7. This was the time when Oracle bought Sun. This has been five years. In five years, we have not seen a single new language feature or a single new API, so you start with Java 6. In between these releases, there was nothing, there was just silence. You won't get some preview features. You won't get some incremental updates. It's just that you wait a couple of years and all of a sudden you get a new version. This has changed with OpenJDK. With OpenJDK, the new schedule started with Java 9. It's now that we have a new release every six months. Two releases a year on a constant cycle. When we look at the lifecycle of the releases, we see most of the releases only are maintained for six months. In between, we have long term support releases. The current long term support releases are Java 8, Java 11, and the next one down here will be Java 17, which will be released this autumn. Actually, the schedule is still the same. Every couple of years, we get a major version with a long term release schedule, but new, in between, we get intermediate versions where we can already test and adapt to the new features.

Sustainable Update Strategy

What is a sustainable update strategy? First of all, really make sure you have a supported JDK in production. Currently, we are on Java 8. Java 8 will probably be supported for a couple of more years. There's really no reason not to update to at least Java 8, if you're running on Java 5, or whatever. This is a very general advice. You should have a clean code base, but in respect to the Java runtime, take care about deprecated APIs. With the recent Java versions, the deprecation annotation has been extended by a new attribute, forRemoval. If forRemoval is set to true, this means that there are serious plans to remove that. This already happened. If you choose to ignore deprecated APIs, one day, your code base will not compile anymore because the API will simply go away. Keep an eye on that. The other thing is internal APIs. There was a bad practice to rely on internal APIs, the com.sun or Sun packages, and they get now encapsulated with the Java module system, and access to them gets more hard. You will get a warning first, and later with the next versions, you will not have access to these internal APIs anymore. Avoid them. There's really no reason to rely on internal APIs, use the public APIs and the Java packages only.

If there are runtime warnings by the JVM, printed to the console, printed to the log files, really take them seriously. They typically give you a hint that you're using two internal APIs, that you're using deprecated JVM parameters. Fix them, because one day it will break. Update your tools. The tools, let's say IDEs, build tools, they did a pretty good job to catch up with the latest Java version. If you update your IDEs, you will have no trouble introducing new Java versions because the tools will most likely support the latest Java releases. Also, update your dependencies. Also, your third-party dependencies might use internal APIs, or deprecated APIs, so typically what they do, they release updates from time to time, and also clean up their code base. To avoid problems with your third-party dependencies, please update them from time to time. Just make sure that the library does not require a newer JDK version than you are currently on. Most libraries are very conservative. Like we at JaCoCo, we still work on Java 5. Most libraries on Maven Central are fine with Java 8.

Safe Update Strategy

When it comes to actually updating your runtime in production, let's say in a large system, you want to be safe. You want to make sure you don't break production. You want to make sure that in case something goes wrong, you still can somehow escape from the situation. This is what I recommend, and what we did several times in large production systems is to do it in two steps. Step one is update your runtime JDK. Test it. Bring it into production. Experience with that. Does it work? Does it fulfill your non-functional requirements? Do you get any problems? Do you see any problems? Only if you succeed with that, take the second step, which is, update your development target. Compile for the new version. Use the new features. Compile bytecode for the new version.

This looks like this. If these are your releases, you will always first update your runtime JDK. Then, for the next release of your software, if you're successful with updating the runtime, you also update your development build chain. This gives you a safety net. Because in case something fails at runtime, during testing, or even in production, you can always fall back to the old runtime and take your time to fix the problem. If you change development version at the same time, or from the very beginning, you open up the gates to use new APIs, to use new language features. There's no way back. In case something goes wrong, you cannot just switch back to the old version that previously worked. This is a conservative approach, especially for large systems where you can make sure that you're safe on the runtime side in production. Once you have that confidence, you also move the development stack to the new version. I really recommend that.

Potential Update Issues

What are the potential issues with updates? The JVM changes from time to time. It has hundreds of command line options, and these options change. They get deprecated. They go away. Semantics sometimes changes a bit. A great tool for that is the VM Option Explorer by Chris Newland, so have a look at that site. You can explore the options for every Java version and see the diff between two Java releases. This helps you to understand what might change in your production configuration, and which JVM options you should consider to replace for something else. During cleanup and modernizing the JDK, the OpenJDK team decided to remove some of the Java Enterprise APIs, mostly in the javax packages. They simply disappeared from the JDK, something like javax.transaction or javax.xml.bind. For example, javax.xml.bind, we use a lot for XML interfaces, where we have interface Java classes that are annotated with javax.xml.bind. You need to find replacements for them in Maven Central. In Maven Central, you will find the APIs and the mutation for all these packages, and you need to properly declare dependencies on these replacements to make sure your application runs with a newer version, where this package has been replaced. This happened mostly with Java 11. With Java 11, all these packages have been removed.

The internal APIs you might have used, get encapsulated. You get at least warnings in newer versions like Java 16. By default, it will not work anymore, so the code will blow up. Once you start the application, you will get an exception, this class is not accessible anymore, and you're done. Take measure for that. There's two options. One option is, do static analysis with the jdeps tool. The jdeps tools can see your class files, your char files, and looks for dependencies on internal classes and reports that, so we can have a complete picture of your code base and see where maybe something is hidden, some internal access. The other thing is, do it at runtime. There's the --illegal-access switch with a couple of options, and one option is the debug option. The debug option will print a detailed report. At runtime if it comes to the situation where your code base hits an internal API, then you can try to fix that.

What to Do With Non-LTS Release

I told you about the non-LTS releases before, so the releases that are only available for six months in between the major long term support releases. What should you do with them? My personal recommendation is to actually test your software with them and identify potential issues early. You will be prepared to see, what problems my software will encounter, maybe what APIs will go away. What do I need to refactor? Where do I use deprecated APIs now? Maybe you also encounter some problems with the runtime itself, like you get a crash, or you get some unexpected behavior. This is a good opportunity to report that to the OpenJDK project so they can fix it. What I do not recommend actually, is to use non-LTS releases in production. Because this means you really need to be able to upgrade your runtime to the next LTS release or to the next development release within six months. Unless you cannot guarantee that, I wouldn't go to non-LTS releases because after six months, you for sure are on an unmaintained version. It's a good opportunity with testing them to prepare for the next LTS release.

What About Preview Features?

One more thing, you might have heard about the preview features. There's a new flag for the compiler and also for the runtime for the JVM, --enable-preview. This will get you access to new language features. You can try out experimental implementations of new language features of new JVM features, and also on new APIs. APIs which are only available with the --enable-preview flag, otherwise, you get a warning. This is really nice to study new features and follow the implementation in the OpenJDK project, and maybe also report feedback. Again, I do not really recommend to run your production with --enable-preview. These features are not yet finalized. They will, and they did in the past, they did change. The idea of the preview is to really get feedback from the community and actually work on that feedback and modify the implementation or the specification according to it. If you rely on preview features, you will have to adjust your code base soon because it for sure will change.

Questions and Answers

Ruiz: Do you have any insights about how to keep up with the latest JDK when you depend on a third-party component that is not compatible with the latest JDK? What is your advice or your thoughts on that?

Hoffmann: First of all, my advice is test early. Really pick up the development releases and be prepared. Typically, these libraries are also somehow maintained, maybe they're open source. If you pick up and identify the issues early, you have a chance to provide proper bug reports and maybe even contribute a pitch, or if it's a commercial thing, notify the vendor early. Then you have some timeframe, some headroom to get the library fixed and updated. Until you, for example, pick the next long term release version. Basically, there's probably some workarounds. For example, if the library uses internal APIs, you can open that up on the command line. In general, my advice is test early and try to fix the root cause. We did this every time. It's amazing how many people actually use open source libraries and complain that they do not work in a specific environment. Especially, in a commercial environment, it looks like there's a huge barrier that people actually contribute back, saying, "There's a real problem. Here's a rapid user, can you please have a look?" That's so easy. I constantly teach people, especially in commercial environments, to do the minimum of contribution to open source environments and just report the issue. It's the first step you do.

Ruiz: Yes, the first step, report it. You don't even need to fix it, just provide feedback.

Hoffmann: Provide feedback. If you're a rapid user, this is the best thing you can do. You don't have to dive in the code at all.

Ruiz: Submit a test case, like a small reproducible example.

Why stick with Java? I want to know your opinion, why Java? We are Java people.

Hoffmann: When it comes to technology, I always try to avoid a fanboy. It's really dangerous. I always try to challenge myself and understand, why it is a good choice, and why not switch to another JVM language, or maybe go to a different runtime. First of all, Java for me has a huge maturity. It has a proven track record of a really stable runtime environment, which gives you a lot of benefits. We have a very active development currently and really evolving to a modern language. Finally, it's about know-how. What skills does your team have? What skills will your team have maybe in the next 10 years? Here, I think Java is a pretty good choice. Maybe you're in a .NET company, and then maybe .NET is the better choice. Picking up, let's say, like random technologies that you pick up from time to time, for example, Scala was very popular some years. Now we have some legacy Scala code, no one wants to maintain it anymore. For me, if it comes to a commercial project, and not just toy projects, I would really consider know-how which is available.

Ruiz: We have to find the balance between maturity, features, and support. There are not a lot of languages that meet all that. One of the things that people complain about Java is that sometimes we don't move fast enough or we don't get rid of the past fast enough.

Hoffmann: I think Java is actually moving too fast, to be honest.

Ruiz: We don't need to migrate or update. Java 8 is going to be supported until 2030.

Do you have plans to upgrade JaCoCo source from Java 1.5? What will be a good reason for doing that?

Hoffmann: Currently, we have no compelling reason for doing that. JaCoCo is a tool which is used in many projects on a huge variety of platforms. Of course, it would be more fun to have Java 8 and have more language features in all APIs, but this is only fun for the maintainers of the library like us. It's not fun for the users that may use it in a Java 6 environment currently. Feature-wise, we haven't seen any compelling reason to do so. JaCoCo works on Java 17, just the implementation of JaCoCo is still compatible to Java 1.5. It could perfectly work on class files with Java 17.

Ruiz: It's still doing its job quite well.

What are the upcoming features that you're looking forward to in the next Java versions?

Hoffmann: Records is something, and the additions to the language, like switch statements. This is something I'm really looking forward to, especially records. This is the thing, because if you used to write enterprise software, the getters and setters, is really horrible. I really hope we can get rid of that soon.

Ruiz: Do you have any other recommendation for the desktop graphic UIs? They started with JavaFX.

Hoffmann: Actually, I was leading a group that was for a huge corporation that was working on the UI strategy. A decision was made that we switch to web interfaces, and that Java will not be the tool of choice for the user interfaces of the future for that specific enterprise. There's a strong trend in this direction. There's lots of know-how, lots of tooling in that direction. My personal opinion, especially for business software, is that we won't see many Java GUIs in the future anymore. I think the web will take that part.

Ruiz: One thing I found is it's hard to convince people to move up Java versions, it's never seen as worth the effort. Any advice to go with admin and say, we should upgrade? Why?

Hoffmann: It's a business decision. I asked the question, how long do you want to run the software? How long do you want to get developers to maintain the software? If you ask this question, seriously, and if you look at the license model of the old Java version, so if you're below Java 8, you have two options. You pay a lot of money for licenses, for updates, or you update at least to Java 11 to have the free alternative you can use in production. In the end, it comes down to money and the ability to execute. Most of the libraries you depend on, the new versions will not support the old JDKs anymore, because they upgrade to Java 8 or maybe even to Java 11. Basically, it's a lifecycle question. If you bother about your product, if you want to maintain it for the next couple of years, please upgrade. If you don't care, don't ask me to add any features.

Ruiz: Actually, it's more about, what are the libraries? Who is going to maintain it, for how long? What are the vulnerabilities? Is there something that will make our software faster, with the performance or something? It has to be compelling reasons at the end of the day, not because it's new. There are several things outside that are new, but that's not a good enough reason.

Are you looking forward to any specific feature in the next LTS? No?

Hoffmann: It's basically records.

Ruiz: Not in the LTS. Actually, with the cadence, we have had a lot of features in the range from 11 to 17, so it's really difficult to pinpoint one. Because when they ask me a specific question, what version? I'm always like, if you think about the accumulated, it's really interesting. If you think about one, it's difficult to pinpoint.

Hoffmann: Absolutely. I'm working on the Java website, and I try to keep track and write them all down. This also helps me to keep track of the latest features and understand them. Some of them are nice, but I don't consider them business critical, like I cannot write the software without a new feature. I wouldn't do that. I'm not a tech fanboy. I really ask, what is the benefit of that?

Ruiz: Actually, it's because you work in this really important system that is not so easy to say, let's do it again. It's huge. You are working with really important things. I think your failure rate should be extremely minimal.

Hoffmann: Absolutely. Also, we are working here at the Railroad. They are used to a very different lifecycle. Such a train has a lifecycle of 20 or 30 years. If you as a software developer tell railroad people, "We just developed the software last year, but we have to rewrite it again this year, because of new technology, and whatever." No one will buy that.

Ruiz: Is there any plugin tool that can be used to analyze what needs to be changed in order to upgrade from one JDK version to a greater one?

Hoffmann: This comes with the JDK. There's two options. One option is to do a static analysis. You can look at your class files, and your compiled class files and look for, for example, internal API usage. This is with the JDepend tool. It is part of the JDK. You don't need a plugin. You can just run it in the command line on your class files, and you get the result. The other thing is the dynamic part. By default, modern JDKs will print a warning on the console. Really take a look at that. If there's a warning, look for the root cause of why it's a warning, because someone thought this would be useful to show you the warning, do not ignore them. You get that for free. You don't need a tool.

Ruiz: Actually there was a question about, should I pay attention to the wording? Yes, at least to look and know what they are telling you. If you don't have the time to fix it right now, but at least be conscious. That will be my step zero advice.

Hoffmann: If you decide to ignore warnings, sooner than later, it will bite you.

Ruiz: Have you had any production issues in the real world system with Java upgrades or dependent libraries. Production issue that environment could have been live or dead, does that make you feel apprehensive? Have you had any issues with the Java upgrades?

Hoffmann: Of course, we had issues with the Java upgrades, but not in production. This is important. We need safeguards. This is this conservative approach. We upgrade the runtime version in production first. If we build against the old JDK, we can always fall back. If you're having a production system, and you do an upgrade to a new version, and probably you have tested it for some time. Even if you encounter the problem in production, you can still fall back to the old runtime, because your code still is compiled against the old runtime. That's why if you have a critical system, I really recommend to do the two steps, like, have it in production for a couple of months. Once you're very sure that this works, you can also give your developers the new JDK version and allow them to use the new features and new APIs. Then there's no way back. Once you open the gate to use new APIs, and use new language features, you cannot backport the whole hot code.

Ruiz: I totally agree with you. That slide is pure gold, because it points the important things, like once you open the bottle and let the genie out, there's no way to put it in. You need enough time to test whatever you need to test in all the possible cases. Yes, super important.

What do you think about using JVM based languages instead of using Java? For example, Kotlin. There are a lot of improvements in this language that are needed in Java.

Hoffmann: Absolutely. First of all, if you use a different JVM language, you get all the benefits of the JVM. The JVM is a great runtime. For me, there's no reason not to use another language. Then it's about know-how, and how your team is structured. If you have a good Kotlin base, and there's a lot of good tools in the meantime, Kotlin gives you lots of benefits also, in terms of safety. It's a good pick to go to such a different language. Again, please ask yourself, do you have the capability to do that? Do you have the know-how? Do you have the developers, or is it just your personal toy project to learn a new language?


See more presentations with transcripts


Recorded at:

Nov 20, 2021