top of page

About JFocus "as a service"


Recently we launched JFocus “as a service”, which offers custom modernization projects for your codebase by combining expert consultancy and the power of the JFocus static analysis and modernization engine. This engine will be the foundation of the standalone JFocus software tool, which will be released in the future. In this blog post, we give some insight into why we offer this service, how it works, and why we think it's a great option for any team that wants to modernize their Java code.




Why?


The first reason for launching this service is to address a clear need in the market. Many software companies and departments are struggling to find skilled programmers to work on their codebase. While they recognize aging code is a problem, they rarely allocate staff to it. When suitable candidates are hired they typically focus on implementing new features, leaving older code in need of maintenance. This creates an ever-growing divide between older projects written in a dated code style and the recent code that everyone loves to work on Our code modernization service can help modernize parts of your codebase without burdening your team.

The service aims to make code modernization more accessible to new clients. Our approach combines expert knowledge with analytics and transformations provided by the JFocus engine. We manually guide the engine's execution to provide tailored recommendations for each client within the desired scope. This allows clients to experience the benefits of code modernization in a controlled project before fully committing to the product at a later stage. You have full control over the scale and cost of the modernization. We can start small and locally, and scale up at a later stage.

Performing modernization as a service also has a clear benefit for the CodeLaser team. The goal of JFocus is to be a tool that revolutionizes the way businesses approach code modernization. By performing the service we gain knowledge of the specific challenges that businesses face with code modernization. This helps us prioritize the right features and functionalities to ensure that our product is designed to meet the real-world needs of codebases of all types and sizes.



How?


So how do we go about performing code modernization? It all depends on your priorities, but as a general rule we first turn to the code’s compilation environment, and then work our way up from smaller detailed changes to more aggregate improvements.


Building the code

A modern codebase has a streamlined build process and this can be a good starting point for modernization. During this first step, the list of external dependencies is established and we compare these libraries to our known list of library upgrades and alternatives that go hand in hand with the required Java version. In conversation with the client, we can target swapping out older libraries for more recent versions or equivalent functionality in recent Java versions.

Take the example of a client that has an existing codebase in Java 8 and wants to move to a modern web app setup with microservices. Typical libraries to use could be Quarkus or Micronaut for the microservices, in combination with a modern Java UI framework such as Vaadin. The overview diagram shows some libraries and their dependencies on Java versions in different releases. The diagram shows that recent versions of both Quarkus and Micronaut and recent Vaadin versions require at least Java 17 to work. Upgrading your code to Java 17 allows for many language-level modernizations. New features such as Records and Sealed classes, improved switch statements, and instanceof pattern matching are all part of Java 17’s capabilities. Using them can greatly improve the compactness of the code. Records and Sealed classes increase the shielding and immutability of data, which in turn improves general safety and reliability during further maintenance.







Modernizing statements

Moving to a recent Java version often makes older utility libraries obsolete, as their functionality is replaced with equivalent JDK implementations. Typical examples are time and date libraries, stream and I/O handling utilities, advanced data structures, etc. The JFocus engine helps pinpoint these possible replacements, which is the start of the statement-level modernization of the code.

Various other improvements can be made pertaining to code-style or novel features of the target Java version. In the case of the Java 17 target, for example, replacing older versions of the abundant instanceof can substantially compact the code. Consistent construction and initialization of objects, null pointer handling, and if/else structure are just a few examples of unnecessary bloat and variation that call for modernization. The detection of possible cases of replacement and modernization is driven by the JFocus engine. It uses functional analysis of the code to detect where novel patterns can replace outdated multi-statement constructs and suggests the correct modernized version.


Modernizing Methods

The next phase of modernization targets methods or functionally grouped statements. This includes improvements driven by the Java version such as switch and stream constructs, as well as detecting functional duplication of methods or blocks. Older code often has frequent outdated patterns for looping and collection access.These can be improved substantially with recent collection methods and streams. Take a look at the following typical example of looping a map collection to remove all entries with a value smaller than a reference value:



Iterator<Entry<Long, Long>> it = removeTargetCollection.entrySet().iterator();
while (it.hasNext()) {
    Entry<Long, Long> e = it.next();
    long elementValue = e.getValue();
    if (elementValue < someReferenceValue) {
        it.remove();
    }
}

Since Java 8, this can be modernized to the more compact:



removeTargetCollection.entrySet().removeIf(
    e -> e.getValue() < someReferenceValue);

or even



removeTargetCollection.values().removeIf(
    v -> v < someReferenceValue);


Combining collection operators with streams allows for even further modernization, for example in constructing complex strings from data, such as SQL queries. The following typical older code …



StringBuilder innerQuery = new StringBuilder();
innerQuery.append("SELECT ");
boolean first = true;

for (String key : masterTable.getIdFields()) {
  if (!first) {
    innerQuery.append(" , ");
  }
  innerQuery.append("mt.").append(key);
  first = false;
}

innerQuery.append(" FROM ").append(masterName).append(" AS mt LEFT JOIN ")
  .append(typeName).append(" AS et ON ");
first = true;

for (String key : masterTable.getIdFields()) {
  if (!first) {
    innerQuery.append(" AND ");
  }
  innerQuery.append("mt.").append(key).append(" = et.").append(key);
  first = false;
}

StringBuilder query = new StringBuilder();

query.append("DELETE mt FROM ").append(masterName)
  .append(" AS mt WHERE ( ");
query.append(innerQuery.toString()).append(")");

return query.toString();

… elegantly modernizes to something like the following, provided that the client insists on building the SQL string explicitly instead of using a dedicated library:



String selectColumns = masterTable.getIdFields().stream()
	  .map(field -> "mt." + field).collect(Collectors.joining(", "));
String joins = masterTable.getIdFields().stream()
    .map(field -> "mt." + field + " = et." + field)
    .collect(Collectors.joining(" AND "));
String innerQuery = "SELECT %s FROM %s AS mt LEFT JOIN %s AS et ON %s"
    .formatted(selectColumns, masterName, typeName, joins);

return "DELETE mt FROM %s AS mt WHERE (%s)".formatted(masterName,
    innerQuery);

Most codebases have substantial amounts of code duplication. While literal code duplication is relatively easy to spot, detecting semantic duplication is the true challenge and a source of costly maintenance. Semantic duplication refers to code that performs the same or similar functions, but is written in slightly different ways. This can make it difficult to identify and consolidate, as the code may not look identical at first glance. However, resolving semantic duplication can lead to significant improvements in code readability, maintainability, and performance.

The JFocus engine uses advanced static analytics and transformations to identify and consolidate semantic duplication where worthwhile. The engine also works on the intra-method level, so consolidation can include splitting methods to isolate frequent functional patterns. However, the tendency to consolidate must be balanced with knowledge of the codebase as it can sometimes be beneficial to keep duplicated code for reasons of clarity or code structure.


Modernizing Classes

Consolidation of duplicated code naturally takes the modernization effort to the class level. Class restructuring can be a key factor in optimally exposing reusable methods with features such as interface default methods. The Record type provides an elegant way to transfer data between classes while protecting it from unwanted mutation and is a prime candidate for introduction into modern Java. The code below shows a typical example of a method that calculated a sum and leaves secondary information about the calculation in an additional collection that was passed as a parameter.


List<String> added = new ArrayList<>();
int sum = computeAndAdd(strings, added);
...

int computeAndAdd(List<String> sourceStrings, List<String> added) {
    int sum = 0;
    for(String source: sourceStrings) {
       if(accept(source)) {
          sum += someFunction(source);
          added.add(source);
       }
    }
    return sum;
}

Using Record the code can be simplified to return a compound, immutable result while improving the readability of the computation method:


Result r = computeAndAdd(sourceStrings);
...

record Result(int sum, List<String> strings) {}

Result computeAndAdd(List<String> sourceStrings) {
    List<String> added = new ArrayList<>();
    int sum = 0;
    for(String source: sourceStrings) {
       if(accept(source)) {
          sum += someFunction(source);
          added.add(source);
       }
    }
    return new Result(sum, List.copyOf(added));
}

JFocus is built on the foundations of e2immu, a powerful static code analyzer equipped to detect various types of immutability in Java code. JFocus can suggest where structures like Records can be introduced during modernization, based on a full scan of the codebase and analysis of the initialization and lifecycle of the candidate Record fields.



Conclusion


The above examples are just touching the surface of a full modernization and are merely meant to give an impression of the types of improvements that can be expected. Our goal is to revolutionize the way businesses approach code modernization, with a combination of expert knowledge and JFocus tooling. Follow the links below for more information about various topics in this post.



Links:


bottom of page