An Update on Application Modernization

Eric Herness
AI+ Enterprise Engineering
13 min readJan 7, 2022

Introduction

After being involved in application modernization for a few years, it is perhaps time to share how our perspective has changed and what the world looks like to us at this point. This blog entry will endeavor to update those interested in the thinking we are using now versus where we might have been before. Things that are also unchanged from the original thesis will be mentioned as appropriate to provide context.

First, it is important to credit the work of Gartner and their 7 options to modernize legacy systems. This work gave the modernization community some guideposts and ideas on how to look at application modernization. That said, I’m often asked why I promote and insist upon using a different taxonomy. My answer is simple. What we’re suggesting is an evolution which is:

1. Targeting cloud with more emphasis and precision. This means fuller treatment of patterns that are more oriented towards migration. This means more precise recommendations as it pertains to cloud provider native services.

2. Promoting Kubernetes as a target more succinctly versus trying to be all inclusive.

3. Going deeper than the Gartner model. Our view is that it is essential and valuable to recommend different techniques for different components of an application. For example, moving a database from an on-premises software model to a cloud service (replatforming) combined with containerizing the Java code (containerizing) for a single application is often a technically viable and financially attractive recommendation.

Our current view is represented here:

After much debate, before departing from IBM, we injected replatform in favor of repackaging, which I’ll get to in more detail shortly. Again, remember, that we’re using these techniques to address a component of the application, not necessarily the entire application. An application component can be a tier, a subsystem, or specific technology that contributes to the overall application. Anything that is somewhat separable and deserving of independent assessment can be a component. Application runtimes, databases, or integration buses are typical examples of application components.

As you will see further into the blog, this approach has helped lead us to the assertion that we should and now do recommend cloud provider native services quite often compared to our prior perspective. Additionally, some cloud providers offer cloud provider services that run on client-owned locations (Distributed Cloud), providing cloud services in cases where applications, data, or both cannot leave the proximity but cloud benefits are still desired.

Let’s revisit these major techniques in order and provide an update on what each really implies in our current thinking and what some examples are of each.

Containerize

In the beginning it seemed simple, containerize it all. Sometimes that is still the right answer and sometimes it is not. There are a few use cases to consider here, none of which are as straightforward and obvious as we might have once thought. Let’s look at java code, other application runtimes and middleware.

Java code

In general, containerizing Java code makes sense. That can be Java code running in application servers (like WebSphere, JBOSS, Weblogic and Tomcat) or roll-your-own Java servers.

What experience has told us is that Java code in these earlier form factors is:

1. Laden with technical debt. Being on back-level Java and JEE levels is very common.

2. Intertwined with legacy control planes. Somewhere, there is likely a control plane orchestrating the JVMs that execute the business logic.

3. The CI/CD approach being used can be all over the map, meaning that automation may or may not be present in any form and that whatever automation that is there, deals in .war, .jar, .ear files.

4. The runtime topologies can be ones where hundreds of applications co-exist on a single JVM (scaled by replicas) or there can be a single application per JVM , all done for good reasons at the time they were initially setup or during challenges that arose during evolution of the application, but the conditions and recommendations have changed with the introduction of cloud computing.

This means, in the end, that it is often the case that code changes to remove the technical debt are necessary. Although infrequently, we have seen the need to change code in order to remove control plane dependencies also. Our experience shows that 80% to 85% of JEE applications required some updating along these lines, to account for changes in java levels, deprecated APIs and these control plane dependencies. Sometimes this effort is minimal, patterned and straight-forward, but it is work, nonetheless.

With this notion now teed up, and the work reasonably well explained via mature tools like Cloud Transformation Advisor, it is important to point out that moving to the modern application server runtimes such as Open Liberty or Quarkus should be done at the same time as removing the technical debt. These runtimes are more aligned to run and perform well in container form factors than are the more traditional WebSphere Application Server Network Deployment (WAS ND) and JBOSS offerings. You’ll also be using these newer runtimes for any Java code you write as part of refactoring, partly because they have microservice friendly frameworks like MicroProfile that accelerates landing on Kubernetes. These modern application runtimes also have a reduced licensing cost when compared to the traditional runtimes.

Containerization also leads to a build out a new CI/CD pipeline for the application. This might seem obvious, but you cannot just use a pipeline that produces .war, .ear and .jar files. For example, there needs to be some actions that creates Docker images and scans those images. This implicitly starts the path of repackaging these applications. It isn’t mandatory to have your pipelines limited to producing large monolithic Docker images that have lots of .war or .ear files. You can and should figure out a more granular level of packaging while you’re at it. This opens the door to enjoying the benefits of autoscaling smaller hunks of capability and will use your Kubernetes capacity more efficiently. This kind of repackaging, within the containerization approach certainly takes a little more investment.

Note, that the original view we had was that this repackaging was a separate and distinct technique, but in the end, this is almost always done as part of containerization so we compressed our original view of repackaging back into containerization.

Further, by containerizing Java code, and relying on a modern app server runtime can provide security benefits. When Log4j vulnerability was published, those that are containerized could just trigger a new build using the new base image provided by Quarkus or Open Liberty and be done. With less modernized applications the work was far greater.

Finally, our experience with these scenarios suggests that making the leap to containers is preferred over interim steps to remove technical debt or just Dockerize. You need to get to the Kubernetes control plane sooner rather than later. If Kubernetes isn’t your control plane for the business logic components of a Java application, then you’ll find yourself on something you have to build yourself or something proprietary.

Other application runtimes.

Any application capabilities that are packaged with or into an application runtime can and do follow a similar approach for containerization. .Net in particular matches this type of model, including the technical debt challenge, the repackaging challenge and to a lesser degree the control panel dependencies. Some ESBs and other runtimes that held integration logic can use a similar approach to get good value from containerization. This includes the light repackaging mentioned previously.

Middleware

For this section, we’ll define middleware as separate address spaces that are leveraged in the existing applications to provide the overall business function supported. Databases, messaging platforms, business process management platforms, business rules platforms and content management platforms all are examples of middleware software. These are candidates to be containerized largely as is. Vendors generally provide containerized versions of this middleware and that is what you should use. They are provisioned onto the Kubernetes cluster once and will likely be managed as they were before, but now on a container runtime. Some of this middleware has been enriched to leverage Kubernetes features like stateful sets and availability. That is good and can be a start on a path to immutability and ephemeral.

In this area, your mileage, in terms of the value realized, will vary greatly depending on how much integration and fitting to Kubernetes has been done. It also depends on your current automation levels that provision middleware. This is probably a topic for another day.

In fact, a better way to handle this class of middleware is to do replatforming, which is covered next. In fact, our thinking has evolved to where we would only recommend middleware containerization in cases where the middleware and the application runtimes absolutely had to be in the same Kubernetes cluster or are customized beyond that which would be supported by the services by the cloud providers. More on that topic is probably another blog for the future.

Replatform

Replatform is the new kid on the block for us and is something that we are now leveraging as the technique for databases, messaging systems, event fabrics, API management platforms and other things traditionally known in the middleware world. Gartner’s replatform seems proper inspiration here and was something we just weren’t as focused on earlier.

The primary pattern here is to use cloud services (public cloud services or services delivered by a public cloud using the distributed cloud pattern) instead of existing footprints which are usually VMs or bare metal. We suggest jumping to this approach instead of containerizing if the cloud service provides the necessary capabilities, both functional and non-functional.

Why would we say this? Because by letting the cloud provider manage these capabilities, it reduces the management costs for the application owners or the shared services providers, whom are usually the IT organization. Cloud provider versions of these kinds of services are often available in configurations that are more secure and perhaps more available than that which was available via traditional on-premises middleware installations. This is at least true of the public cloud manifestations of these services and more and more we are finding that these services are available in private clouds in the same way via the distributed cloud approach.

The work of replatforming, however, is not to be trivialized. For databases, getting to a proper managed service might be a ‘replatform’ from Oracle to PostgreSQL or from IBM MQ to the messaging service of your favorite cloud provider. In most cases, there are standards like JDBC, JMS and others that isolate your application code, but you still must figure out how to provision these cloud services, make sure they are integrated with your security architecture, conform to your organizations networking isolation guidelines and the rest of the engineering needed to let them be production grade. Watch out for cases where these cloud services are not at the same version of the underlying capability as you might have enjoyed (and needed) in the on-premises rendering. Also be aware that unlike containerizing, when replatforming, you have to make sure they are logging to the same place as your applications, that they are integrated to the eventing model. Most of these cloud services are in fact pretty well integrated and you’ll find using them easier than standing up these middleware capabilities on your own in VMs on your favorite cloud provider.

Again, while not difficult, you should and can provision instances of these services as part of provisioning your application.

We really like replatforming to reduce complexity and inject consistency when modernization 100s or 1000s of applications to the cloud.

Finally, consider your regulatory and compliance requirements when looking at cloud provider managed services. Make sure you know where the data is being stored and managed. In most cases, these are features of the cloud provider managed services, but check it closely to be sure.

Note that for the most part, replatforming will be a complementary technique for business logic that is being containerized and possibly enriched and refactored, although in the latter, you may or may not reuse existing middleware capabilities in the new application.

Refactoring

Refactoring remains the most aggressive technique and can apply to business logic, integration logic and the data layer of an application. Gartner probably pays more attention here and uses refactor, rearchitect and rebuild.

For us, this is where you are pursuing 12-factor and the value of a microservices architecture, or at least as much of that as you want to leverage. What we’ve learned over time is that refactoring often needs to be thought about in phases. This is where the common technique of strangling fits. Strangling implies there is an incremental approach. An incremental approach, when mission critical applications are the topic, means a coexistence layer. Coexistence layers can sometimes last for years and often are as complicated as any other piece of the application architecture. This is yet another topic that needs at least a separate blog if not a bunch of them

Refactoring is also the time in which you should strongly consider which kinds of middleware you want to leverage in the new world. This is your chance, for example, to isolate integration logic or to externalize business rules. There is also a need to think about data and producing events so that current and future needs for analytics and AI can be met. Finally, refactoring is the technique that allows you to re-evaluate your unit of work handling. Do you really need all those two-phase commit transactions? Probably not, but you still have various consistency and integrity constraints to deal with. The optimist says this is a great chance to upgrade the architecture and do a full rebuild while the budget often dictates that you cannot refactor all of your applications or even an entire monolithic application.

The above discussion is in large part about major refactoring. There can also be more minor refactoring done that can add significant value. Imagine just rewriting a particular algorithm or the implementation of a particular operation which the application leverages. In the latter case, you can redo one of the queries in a modern language using a different, query-friendly, database. You can also break a monolith into chunks to achieve more efficient scaling.

Bottom line is that refactoring can mean quite a few things, but none of them are super straight-forward and most will rip up or replace pieces of your existing application.

Externalize

This technique for modernizing is probably underutilized. It isn’t as exciting or glamorous as some of the others. This is all about exposing existing capabilities so they can be reused by applications. What we’ve learned or in some cases should have been more precise about in the past is:

1. Don’t bank on reusing a single API in many different applications. Externalize doesn’t means expose one API for all consumers.

2. Externalize can mean exposing query capabilities or publishing topics and related event definitions that can be subscribed to. It is not just about standard REST.

3. The best tools in this area, typically API Gateway offerings, will let you create some reasonable APIs without writing a lot of code. Those are super valuable but limited in the use cases they support. Once you get into writing a lot of code to expose an API, you are bordering on the Enrich topic, covered next. Always use an API Gateway and leverage other tools as they provide value.

4. Watch out for the additional compute and storage resources required by the externalized capabilities. Remember, you didn’t do anything to make them run faster or scale better internally, but you made them easier to call. That can mean a lot more volume.

Enrich

Just add more capabilities to the applications you have. New capabilities should be done using you preferred blend of 12-factor. These capabilities should be targeting containers and cloud services. For example, you can add a call out to a microservice that returns some more current or AI derived insights.

Without restating the best of how to create microservices, I’ll just mention that it is useful to:

1. use an API gateway to expose services that are created as part of an enrich activity. If an application leveraging the enriched service is refactored, this will make for less work in the long run.

2. publish events as appropriate to make known whatever the new service has done or discovered. Again, as more refactoring is done, expect even consumption to become a more predominant way to build scalable systems.

Combinations of Techniques in Sequence

The previous enrich example and our general experience suggests that chaining together a couple of techniques makes sense and is more efficient. Refactoring and enriching can benefit from having the original application containerized first. In fact, we wouldn’t suggest big refactoring activities that continued to target VM or bare metal deployments. Embrace containers and Kubernetes.

Whether or not you pump the brakes at containerize and go into production like that, waiting until a second iteration or phase to refactor or enrich is a business choice. The investment made to containerize will be a good one in almost all cases. In fact, just containerizing and then observing the application in production, with the logic now running in containers will often provide insights and guidance as to where the next investment should be made.

Migration techniques

This is not the time to dig into the migration techniques, but they exist, and they are complementary to the other techniques I have discussed here already. Maybe we’ll get into those in a later blog entry also.

For now, it is good enough to just realize that these migration techniques are:

1. Focused on getting from one environment (usually on-premises or private cloud) to another. The target is a cloud platform (private cloud, distributed cloud or public cloud).

2. Complementary to the other modernization techniques. For example, if you are containerizing the Java code, you may or may not need to take the database with to the same target cloud using virtual to virtual or physical to virtual. In this case, these migration techniques are good options when the target cloud might not have a service for the specific database that the application requires.

Migration can also be combined with modernization techniques in different sequences, which I covered here last year in a fair amount of detail.

Conclusion

Application modernization and looking at modernization in general from an application lens continues to be a valuable approach for enabling business transformation. We continue to see great value in modernizing different components of an application using different techniques. This not only makes sense technically, but it makes sense financially as more and more applications are considered modernization candidates.

The modernization techniques nearly all enable and demand CI/CD automation, which results in more velocity for most situations. The opportunity to automatically provision application components and even cloud provider native service instances can also be a way to improve consistency and quality as well as leverage usage-based consumption in public clouds.

Our experience has taught us many things about that which is practical and reasonable versus that which is theoretical and aspirational when it comes to modernization. We love to hear about what others are experiencing and any new ideas that are emerging from ongoing experiments and projects.

Thanks to Ryan Claussen and John Alcorn and Julian Petriuc and Greg Hintermeister for their input and review.

--

--