Article

Choose the right Java runtime for the job

Explore and understand runtime history and architecture style characteristics to make an informed runtime choice

By

Graham Charters

Introduction

The IBM acquisition of Red Hat means that the joint company now has a number of Java application runtimes, including:

This has inevitably led customers to ask two questions:

  • Which runtime should I choose?
  • When will 'runtimes A and B' be rationalized?

The answer to the second question is simple -- there are no plans to remove or merge any of the runtimes. Each has a significant and loyal customer base who needs continuity of the runtimes they're using. Efficiencies of development are already achieved through collaboration in open source projects, such as SmallRye and Apache CXF.

This article will address the first question. The answer is not as simple as, "Use runtime X" or even: "For microservices, use runtime X." In order to choose a runtime, or runtimes, it's important to understand three things:

  • Your existing application estate, business needs, and strategy for those applications
  • The application architecture style strengths of each runtime
  • The application architecture styles you expect to employ

The subsequent sections of this article detail the latter two points. The article also discusses how to choose a runtime based on all three.

The evolution of runtimes

To understand the characteristics of each runtime and how they might apply, it's worth looking back at their history, as this greatly influences the types of applications for which they are best-suited.

Traditional enterprise applications

Commercialization of the World Wide Web and standardization of Java application server technology led to a number of vendors creating application server products. WebSphere Application Server, and what became Wildfly and JBoss EAP, were first released in the late 1990s. The hardware on which they ran had modest compute power by today's standards -- networks were significantly slower and had yet to standardize around Ethernet, and memory and disk capacities were limited (it would be 10 years before the first 1TB HDD was released). Software delivery was also dominated by Waterfall and associated engineering practices. This naturally led to the development and delivery of monolithic applications. Of course, in those days they were just referred to as "applications" because there were no other types. It's worth saying up front that monolithic applications are not bad -- unmaintainable monoliths are bad, and it's possible to build unmaintainable systems using any architecture style. In the early 2000s, VMs emerged as an approach to simplifying server deployments, but clustering was done in a way specific to each runtime type that you deployed (for example, WebSphere ND Cells).

The constraints, practices, and long delivery cycles led to Enterprise Java runtimes being optimized for long, stable, performant up-times; once you deployed the server and application, you wanted it to just keep going no matter what.

Business agility demands runtime agility

In the years that followed, ever-increasing demand for faster and more scalable delivery drove significant advancements in infrastructure and methodologies. Processors, network, and storage all grew in speed and capacity, and software delivery embraced agile practices. The push for faster time-to-market led to increasing demand for lighter-weight runtimes that aided the goal of delivering applications more rapidly. In response to these requirements, in 2012, IBM created WebSphere Liberty.

Liberty took a fresh look at how to address the demands of rapid application development and delivery: the needs to be lightweight, simple to configure, simple to deploy, and support emerging best practices and tools. The applications developed at this time were still predominantly monoliths but, with Liberty's help, easier and faster to deliver. Liberty started out supporting simple Web applications, but based on customer demand soon grew to support the full Java EE capabilities. Liberty's ability to install and configure 'just enough runtime' -– only those capabilities required by the application -- meant it didn't suffer from the runtime bloat often associated with enterprise application servers. Predating containers and container orchestration platforms, Liberty also added the clustering and health management capabilities (based around Liberty Collectives) demanded by enterprises.

Over the next five years, open source became an increasingly important aspect of software adoption. In support of this shift, in 2017, Liberty moved to an open source-first development model by open sourcing WebSphere Liberty as Open Liberty under the Eclipse Public License.

Architectures for fast, flexible, scalable deployments

Many monolithic applications grew over the years and often any consideration for their modularity was lost, making them difficult to maintain and extend. Businesses seeking to further reduce time to delivery and increase scalability for far greater workload demands found themselves constrained by the monolith's inherent "deploy all, scale all" nature. To achieve greater flexibility and scalability, these applications needed to be re-imagined as many smaller units, which could be developed, deployed, and scaled independently, namely microservices and functions (often deployed to clouds in Functions as a Service [FaaS] environments).

The shift from one monolithic application to many microservices or functions has driven demand for technologies that both simplify operations and reduce infrastructure costs. Containers and Kubernetes-based container orchestration platforms, epitomized by Red Hat OpenShift®, have rapidly become synonymous with microservices. Containers enabling lightweight isolated deployments, and Kubernetes enabling consistent container deployment, management, and clustering, removing the need for runtime-centric clustering approaches, such as ND Cells and Liberty Collectives.

The growth in popularity of microservices led to demand for new API standards, and so a number of vendors and Java user groups got together, resulting in the creation of the Eclipse MicroProfile specifications. MicroProfile rapidly evolved to provide a full set of open microservice APIs, with a number of runtimes also providing support, including Liberty, WildFly, JBoss EAP, Payara, TomEE, and Quarkus.

The drive for finer-grained components deployed as microservices or functions has led to increased demand for lighter and faster runtimes. In 2019, Red Hat released Quarkus, a runtime tailored specifically for these architecture styles. In doing so, it firmly positioned Quarkus for new functions and microservices, only supporting the subset of the Java APIs covered by native compilation.

Liberty and Quarkus both offer great JVM-based runtime profiles for microservices, responding to first requests in around a second. Additionally, Quarkus offers a compile-native capability, giving an execution profile well-suited to functions, where optimization for a single request is important.

Spectrum of architecture styles

The past few years have seen a strong focus on the delivery of microservice architectures. As with any new concept, the industry goes through a discovery process and, ultimately, learns how best to apply it. Before reaching this enlightened state, there is a tendency to over-apply a technology, leading to poor outcomes. Gartner refers to this learning process as the "Gartner Hype Cycle" and currently positions microservices at the "Trough of Disillusionment." This doesn't mean that microservices are bad; it just means that we're at a point where we are only just beginning to truly appreciate where microservices are appropriate and where they are not.

A quick search of the internet reveals a growing number of articles debating monoliths, microservices, and functions. There are also examples talking about styles between monoliths and microservices, using terms such as "macroservice" or "macrolith." In some cases, this is a reaction to over-decomposition into microservices that are too fine-grained, leading to an overly complex system where the complexity outweighs the benefits. In others, it's a direct attempt to find the right balance between the functional and non-functional characteristics of each architecture style. High-profile examples of companies changing architecture styles over time include Uber teams now deploying coarser-grained macroservices as well as microservices, and Segment, who switched from a monolith to microservices and then back to a monolith.

What's clear is that we are seeing a growing appreciation for the pros and cons of different architecture styles, and we expect that to continue to increase over the coming years. Rather than assume all applications will be microservices, a better understanding of the application needs, infrastructure, and team capabilities will inform the choice of an appropriate architecture style. There will be an increased openness to deploying multiple architecture styles and also to evolving between architecture styles as needs evolve.

Characteristics of architecture styles

When choosing which architecture style is likely to be appropriate, it's important to understand their characteristics. The following shows some key characteristics and how they vary between the different architecture styles. It's important to understand that some characteristics are not automatically achieved by the choice of architecture style; they are potential benefits that require technological or organizational investment in order to attain them. For example, if you don't invest in automation technology, you're going to struggle to deliver microservice updates more frequently.

Diagram showing how non-functional characteristics vary as we move from coarse-grained architecture styles to fine-grained architecture styles

Starting at the bottom-most characteristics, lets take a look at them in more detail:

  • Pricing: There are pros and cons to CapEx (Capital Expenditure, a one-off up-front cost for acquisition) and OpEx (Operating Expenditure, a regular expense entitling/covering use). Functions as a Service is inherently OpEx and with the most fine-grained measurement of any model. Monoliths are often CapEx, but there are ways to smooth the cost to more of an OpEx model (for example, Committed Term Licensing).

  • Delivery frequency/complexity and operational complexity: These both relate to the fact that as we move from coarse-grained to finer-grained architecture styles, we're increasing the number of artifacts that we create, deploy, manage, debug, and service. There's cost associated with this, but also benefits.

  • Modularity: One of the big downsides to monoliths is the difficulty in making them modular and preserving that modularity over time. As we move from coarse-grained to fine-grained we further decompose an application into smaller and smaller parts, but also introduce process and network boundaries as a way of enforcing modularity. This increases the chances of creating more loosely-coupled, flexible solutions. A word of caution, though, as some have found it very easy to create a distributed monolith –- the worst of both worlds (there's no substitute for good architecture).

  • Network demand and fault isolation: These two come hand-in-hand. With the distribution of more and more components comes the increased use and reliance on the network. Network performance becomes a key gating factor on overall solution performance. Network reliability also affects overall solution availability. Increased isolation helps avoid cascading failures taking down the whole solution, but defensive fault tolerance techniques, such as bulkhead, retry, and fallback, need to be employed to exploit the isolation.

  • Runtime demands: Different architecture patterns favor not only different runtime characteristics but also different approaches to using the runtime. For coarse-grained applications, delivery cycles are typically longer and therefore favor runtime stability over a long period of time. The priority is for the runtime to perform well for the longer duration over which the application is deployed. It's also typical for the server to be something you directly manage and optimize, and then deploy one or more applications to. For finer-grained applications, because the delivery cycles are shorter, the number of requests fewer, and the number of instances much higher, the focus is on runtimes being small and fast to start. There is also a shift from server-centric deployment to application-centric, where the server is brought along with the application (for example, in a lower container image layer).

    One final point that we'll explore in the next section is the correlation between API needs and the architecture patterns. For coarse-grained applications, you typically use a lot of different APIs in a single app, whereas for finer-grained, each function or microservice has more modest API needs.

Choosing runtimes

We've seen how the history and evolution of each runtime has strongly influenced the architecture styles for which they are best suited. We've seen how enterprises have a growing appreciation of the benefits of different architecture styles and how many will be flexible in their use. We've also seen many of the characteristics associated with each architecture style and how these can be used to help decide which styles to use, and quite possibly mix or change styles over time. But what does that mean in terms of runtime choice?

Based on the history and evolutions of each runtime, the following figure shows the architecture styles each runtime is applicable to, plotted against the 'API needs' of an application (the API needs of an application tend to be correlated with the 'size' of the application).

Diagram showing which runtimes are suitable for different architecture styles based on the API needs of the applications

This diagram shows that, whichever architecture style is chosen, IBM and Red Hat have it covered. On closer inspection, we see that there are three options for monoliths (traditional WebSphere Application Server, JBoss EAP, and Liberty) and two options for microservices (Liberty and Quarkus). However, rather than this being a discussion about which is best for a specific architecture style, it's important to consider the heritage of each runtime, the strategy for each of your applications, and the architecture styles you're likely to use. The following example scenarios should help:

  • If you have existing monoliths you want to keep working and stable with minimal engineering investment, then they should remain on traditional WebSphere Application Server and EAP.

  • If you're looking to deploy new functions and new microservices, then the targeted APIs and native compilation of Quarkus make it a good choice. Quarkus's native image capability will give you the runtime characteristics ideal for functions.

  • If you're looking to modernize your existing enterprise applications, then the lightweight full-Java runtime of Liberty is a good choice. These applications are often written to Java EE, so choosing a modern runtime with full Java EE capabilities will save you in the long-run. If you're modernizing from traditional WebSphere Application Server, then WebSphere Liberty also provides APIs designed to streamline this modernization path.

  • If you're looking to create new modern monoliths, new microservices, or anything in between, then the capabilities and flexibility of Liberty make it a good choice. This is because Liberty was designed as a modern full-Java runtime for monolithic applications, as well as being optimized for lightweight, high-performance microservices, giving you the flexibility to choose and switch between these architecture styles.

Summary

This article has shown how industry appreciation for different application architecture styles is evolving to a more nuanced view, where architecture styles are chosen based on the business needs for the application, and technology and organizational capability. This article also discussed how a Java runtime's history strongly influences its applicability to the different architecture styles. Finally, through the use of a small number of scenarios, the article has provided an approach to selecting the right runtime, or runtimes, to suit your preferred architecture style, or styles. We hope you have found this useful.