Application development practices have changed with the adoption of software supply chains. Modern apps are assembled from open source software (OSS) and third-party dependencies. Dependencies are changing frequently and evolve at their own pace. This also provides a larger attack surface for bad actors to exploit, demonstrated by continued growth of common vulnerabilities and exposures (CVEs)—surpassing 25,000 in 2022.
The result: Every organization has a living codebase, constantly evolving and growing in complexity—and much of it outside their control. It’s like shifting sands where every grain is a dependency; your footing uncertain. Not upgrading and remediating your modern apps regularly, including all the dependencies, can lead to critical security, compliance, and performance issues. Attempting to manage a living codebase manually is error prone and untenable. It’s common for costly and code-breaking refactoring and migration efforts to be back-logged in favor of app improvements and new features.
If you could accurately visualize and analyze your entire codebase—this complex, interdependent software supply chain—then automate the work of software modernization, you would have a new superpower in your organization. You could regularly make safe, sweeping changes that substantially improve quality, security, and cost of code. Your developers could stay on top of technical debt and be more productive in delivering business value. That’s what continuous software modernization is all about.
Use cases for continuous software modernization
While developers are both writing their own code and assembling applications from various third-party sources, a significant part of their workday goes to updating and fixing existing software—which often crosses repository boundaries. This work is time consuming and tedious with many hands making the same changes in multiple locations. It’s also the critical, necessary work that keeps a codebase inherently secure and aligned within an organization. It’s work that’s ripe for automation.
The use cases for continuous software modernization range from those highly repeatable, frequent transformations to larger transformation projects. Automation is critical for these use cases:
- Code quality updates—cutting through the noise of scanning tools to stay continuously compliant and consistent with team and framework best practices (and alleviate the code smells)
- Software bill of material (SBOM) tracking—automating the creation of SBOM documentation, including all dependencies and metadata (great for producing a CycloneDX BOM)
- CVE remediation—automating the search and fixes of CVEs across your codebase for quick resolution
- Dependency management (aka migration engineering)—automating the move from one software and framework version to another (including all cascading dependencies) across repositories to keep up with the constant version churn in third-party repositories
To be clear, a key precept of a continuous software modernization practice is the automation of code searches and updates, eliminating or significantly reducing developer disruption. All of these use cases are possible with Moderne.
Moderne pioneering continuous software modernization
Moderne changes the way you integrate software upkeep into your developer workflow—providing fast, accurate, and automated code searches and updates. (Yes, we fix the source code.) The Moderne platform sits alongside the software development lifecycle and replaces manual, tedious work without disturbing your existing DevOps system. There’s no need to alter your continuous integration, artifact publishing, or deployment activities.
The Moderne platform operates seamlessly with OpenRewrite—an open source project that provides expert code transformation recipes. With the Moderne platform, you can run recipes across any number of repositories to accurately find and fix issues. You’ll see the estimated time savings to fix the issue across the run, enabling you to prioritize recipes that have the greatest return on investment (ROI) for your organizations. The Moderne platform will correct or update the code, writing it back out to source code retaining your conventions.
Development teams can examine diffs and issue commits or pull requests (PRs) that can be reviewed and put through the usual CI/CD pipeline. You decide when the change goes to production. And if you delay a release, that’s okay because the automation can be rerun anytime, keeping the migration mergeable and fitting into the natural rhythm of the team’s work.
Teams can better understand the impact of software migrations and fixes at a large scale, and simultaneously implement fixes en masse. In addition, they can better coordinate changes across repository boundaries—particularly helpful when your producers and consumers of the change are in different organizations.
You’ll see your stories for technical debt increase, but since these are being managed through automated fixes, you’ll also see a corresponding increase in value stories from more productive (and likely happier) developers.
Continuous software modernization vs. code scan and search tools
To keep CVEs at bay and reduce code smells, you’re probably doing a regular cadence of code scanning (including SAST and SCA tools). One recent study tells us that 90% of apps are scanned more than once a week, and there’s been a 31% increase in the use of scanning tools since 2018. With these scanning tools, developers can see issues and, when possible, fix them earlier in the software lifecycle.
The problem with these tools is that they report a lot of data—thousands of lines of technical debt—including false positives. Additionally, while the tools offer instructions for making source code changes, they don’t do the work for developers. Development teams can drown in the scanning and alert noise. Eventually the volume of code and pace of change overwhelm teams, stifling an organization's ability to innovate.
Let’s take a closer look at these commonplace scanning tools. Most of them operate using a full-text search, the simplest form of search that can be ambiguous and inaccurate. And some of them may produce and operate on an Abstract Syntax Tree (AST). As the name suggests it’s an abstraction of code and lacks formatting, semantic awareness, and resolution of dependencies. For example, when identifying an issue such as Log4Shell or applying logging best practices, a scanning tool will be looking for patterns like Logger. But the AST doesn’t have enough metadata to be able to identify this Logger coming from Log4J or other popular logging libraries. So it will overreport and have false positives identifying other Logger types. This is extra work for developers to examine.
Continuous software modernization with Moderne offers source code search and transformation that is based on compiler-generated data that is a technology leap from the AST. We call this the Lossless Semantic Tree (LST). This sophisticated code representation contains rich semantic information of your codebase—which is 100% accurate for matching patterns. It greatly reduces the search volume and preserves code format for accurate auto-fix.
Key characteristics of the LST include:
- Fully type attributed—retains more detail about each element (including classes that are typically behind the scenes of developer purview with a text-based search), which enables accurate pattern matching and reduces search volume to exactly what you’re looking for.
- Format-preserving—enables the LST to be printed back to source code while retaining your conventions. Code changes look idiomatically consistent in the context of each repository.
- Serializable—enables an LST to be transferable to a data source for later querying and action, which makes refactoring work faster and more scalable.
These capabilities are foundational for scaling code search and transformation with full context on code versions and alignment organization-wide—no need for additional databases or indexing of code.
Continuous software modernization vs. generative AI
One of the more recent questions we hear is what is the difference between continuous software modernization and generative code AI. For this, we consulted an expert:
This was not a bad comparison from ChatGPT. But to summarize, continuous software modernization is authoritative, automated code transformation. Generative code AI is a suggestive authorship tool for code creation. A continuous modernization solution like Moderne is looking at the existing base of source code to automatically discover and fix issues for developers. Code AI tools are supporting individual code development efforts at the IDE.
The Moderne engineering team has found use for code AI to aid in creating code transformation recipes, but it’s an imperfect coding partner. I saw a Tweet that captured the intent of a code AI tool well: “It can be very useful at times, not that much help at times, and sometimes we just ignore it altogether.”
How do you start a practice of continuous software modernization?
We often get asked how to introduce continuous software modernization to an organization, particularly the actual automation of code changes. We suggest that you start with smaller changes to build confidence in the practice among development teams and demonstrate how it can positively impact their day-to-day work. This can include code quality clean-ups or isolated CVE fixes. Once success is shown in these areas, you can build up to minor framework updates or patches, and then to more significant code migration work.
The idea is for all of these types of changes to become a continuous, confident motion for your organization. We see many organizations adopting a weekly cadence of using Moderne to update code dependencies and fix code smells—removing the burden of infrequent, complicated code migrations and keeping the codebase inherently more secure and clean.
Here’s an example flow of updates and migrations (from simple to more complex):
- Improve code quality and readability
- Fix common mistakes
- Eliminate legacy patterns and minor performance issues
Logging best practices
- Improve performance by preferring parameterized logging to string concatenation
- Improve error reporting by using exception specialized logging invocations where applicable
Dependency management
- Upgrade Maven dependencies to keep dependencies patched & up to date
- Exclude test dependencies like JUnit from the compile scope
- Remove redundant explicit dependency versions to clean up Maven poms
- Update Gradle Wrapper to keep Gradle itself up to date
Security enhancements and discovery
- Find secrets like passwords, encryption keys, access tokens
- Use secure random number generation
- Java security best practices
Modernize test frameworks
- Junit 4 to 5 migration
- Mockito 1 to 3 migration
- Migrate JUnit assertions to AssertJ improved readability and consistency over stock JUnit assertions
Major migrations
You can try out these modernization recipes at public.moderne.io on open source codebases. Or contact us to learn more about how you can gain the continuous software modernization superpower for your organization—and start seeing immediate value.