6 · Modernization options

What you can run, and on which runtime

Modernize offers what your repository qualifies for, and the list behind it is data rather than product code. This section covers where the list comes from and what is on it today, grouped by the runtime it applies to.

The Modernize screen

Modernize turns the analysis into a set of cards. Every card is there because of something found in your source, and it shows you what that was.

The Modernize screen, grouped by runtime. Each card carries a detected: chip naming the facts that qualified this repository for it.
The Modernize screen, grouped by runtime. Each card carries a detected: chip naming the facts that qualified this repository for it.

On the .NET repository above you get Decompose to microservices, Govern this API, Security and dependency hardening and Clean up AI-generated code. Each card has a detected: chip — C# · 8 endpoints · 6 entities — naming what qualified it.

The cards say what you would get rather than naming a category. "Derive the true OpenAPI 3.1 contract from the code, close the drift against the published spec, enforce the house rules, and add in-process conformance tests plus CI so the contract can't lie" tells you something. "API governance" does not.

What is ready and what is not are both on the page

Cards further down are marked as coming rather than ready, and say so on their face. Nothing is offered as available and then turns out to be a waiting list.

The playbook library

Behind those cards is the library itself, with the runs grouped by what they apply to.

The playbook library. The cross-runtime ones apply to any stack. The runtime sections below are there because of what this repository is written in.
The playbook library. The cross-runtime ones apply to any stack. The runtime sections below are there because of what this repository is written in.

The cross-runtime ones work on any stack: compare against another repository, consolidate microservices, migrate a database in any dialect, cut deployment cost, move services to serverless, and the test-automation set.

The .NET section is there because this repository is .NET. Other runtimes bring their own.

Each row is tagged with what kind of run it is, so a run that adds new capability is easy to tell apart from one that changes what you already have. Rewrites, which produce a new codebase rather than changing this one, are a third kind and have their own entry point.

How an option gets there

The list is a map file, not product code. The workbench has no built-in knowledge of any particular technology. Everything it can offer is described as data.

A rule is two things

Each option comes with a way to detect it, and each rule has at most two parts: which files to look at, and optionally a pattern their contents have to match. Rules combine as all of these or any of these. That is all there is to it.

Open a project and one scan runs every option's rules. Only the ones whose evidence actually turns up become cards, which is why each card can print the fact that qualified it. Nothing is offered just because a file has a certain extension.

The card is in the map too

An option's name and description sit next to its rules, so the screen can be drawn without loading anything heavy. The run itself is fetched when you click it.

Why there are no totals on this page

Adding an option means changing the map: the patterns, the card, and the run it points at. Installed copies pick that up over the update channel without a new release of the application. What follows is what stands today, and it will grow, so nothing here is counted.

How to read the tables

Options with no marker can be run today. coming means the option shows up in the product as a visible but disabled card, still gated on real evidence from your repository, so you only ever see the ones that would apply to you. in progress means it is being built now.

Offered on every runtime

Some options are offered wherever the runtime is supported, because the problem they solve is not specific to a language.

OptionWhat it does
Security & dependency hardeningClose the security gaps: dependencies with known advisories, secrets left in config, weak authentication and data-protection defaults, and the injection and exposure problems that apply to this stack. Fixed in blocks you review, with scanning added to CI.
Clean up AI-generated codeFind and fix generated slop before it ships: dependencies that do not exist, copy-pasted duplication, dead and unreachable code, placeholder stubs, silent TODOs, and calls to APIs that were never real. Reviewed block by block, with gates so it cannot come back.
Uplift the runtimeMove the project onto the current version of its runtime and framework: the language or SDK version, the dependencies, old project formats, and the breaking APIs that come with it. In place, not a rewrite.
Set up CIBuild or improve a CI pipeline for this runtime: build, test and coverage, lint, and a security scan, committed as a workflow you review.
Set up CD / deployContainerize the application and wire up delivery: a multi-stage image, deploy manifests, and a build, scan, push and deploy pipeline, with health checks, metrics and structured logging.

Security hardening is one option but not one set of checks. It runs whatever tools that runtime has: dependency advisories and injection analysis on the JVM, the npm audit trail and prototype-pollution sinks for JavaScript, module vulnerabilities and insecure patterns for Go, advisory and licence policy for Rust, misconfiguration and over-permissive access for Terraform.

By runtime

Beyond the ones above, each runtime brings the work that is specific to it.

.NET

OptionWhat it does
Decompose to microservicesSplit the monolith into services you can deploy separately, with bounded contexts, a database per service, sagas, and a current .NET and Aspire target.
Upgrade .NET to the latestMove the projects onto the current .NET line, whether they are on .NET Framework or a few versions behind. In place, in blocks you review.
Govern this APIWork out the real OpenAPI contract from the code, close the gap against the published spec, enforce your house rules for errors, pagination and status codes, and add conformance tests so it cannot drift again.
Move stored procedures to .NETPort Oracle PL/SQL and SQL Server T-SQL routines into EF Core or Dapper methods, working from the real routine structure and parameter directions rather than a text scan.
Prove the ported procedures behave the sameRun a test matrix against the original procedures and record everything they did: output values, returned rows, errors raised, rows written. Then replay the same matrix against the ported C# and fail on any difference.

Java

OptionWhat it does
Decompose to microservicesSplit the monolith into services you can deploy separately, with a database per service and a current target stack.
Upgrade Java to a current LTSMove onto a current long-term-support Java in place: the compiler level, the build toolchain that has to be current for the build to run at all, and the few places where an API left the JDK.
Rewrite legacy Java in modern idiomsRe-express Java 7 and 8 era code as a new codebase next to the untouched original: records, sealed types, pattern matching, the modern date and time API, and the jakarta namespace. Choose the in-place upgrade instead if you want the same code on a newer JDK.
Struts / JSF to Spring Boot comingRetire the old web layer. Actions and managed beans become controllers and services, and the pages move to a modern view layer.
Refactoring & code health comingRestructure, adopt modern idioms, and pay down technical debt.
CI/CD pipelines comingAutomate build, test, scan and publish on every change.

Scala

OptionWhat it does
Framework upgradeMove off frameworks that are dead or out of support onto maintained ones.
Refactoring & code healthRestructure, adopt modern idioms, and pay down technical debt.
Fixes & stabilizationClear known defects, deprecations and flaky behaviour.
Cloud & deploymentContainerize and ship to modern infrastructure.
ObservabilityStructured logs, metrics and traces.
CI/CD pipelinesAutomate build, test, scan and publish on every change.
Governance & complianceEnforce standards, licensing and policy, with gates that leave an audit trail.

Node and TypeScript

OptionWhat it does
Decompose to servicesSplit the monolith along bounded contexts, taking services out one at a time behind the API you already have.

Python

OptionWhat it does
Decompose to microservicesSplit the monolith into services you can deploy separately, with a database per service and a current async target stack.
Python 2 to 3 comingFinish the move off Python 2: syntax, renamed standard library, the bytes and string boundary, and a supported set of dependencies, checked module by module.

Front-end frameworks

OptionWhat it does
Angular modernizationAngularJS 1.x to modern Angular, or a stepwise update within modern Angular. It works out which of the two applies.
Vue 2 to Vue 3 comingMove off Vue 2, which is end of life, to the Composition API and current tooling, component by component.

PHP

OptionWhat it does
Legacy PHP modernization comingBring 5.x-era PHP onto the modern line: removed APIs replaced, typed code, Composer packaging, and a supported framework core.

Go · Rust · C and C++ · Terraform · COBOL

None of these has a modernization option of its own today. Go, Rust, C, C++ and Terraform get everything in Offered on every runtime. For C, C++ and COBOL the real work is a change of language, which is Moving to another language.

Not tied to a runtime

Some work is about the estate rather than the language it is written in.

Across repositories and databases

OptionWhat it does
Compare against another repositoryRead a second repository with the same pass and compare every layer: technology, structure, routes, contracts, data model, dependencies, tests, health and activity. You get what is shared, what differs, which side is stronger where, and whether the path is a migration or a merge. Nothing is changed. You get a report.
Migrate database (any dialect)Move a relational database to a new engine: schema, types, constraints, views, sequences, and stored-procedure logic. The source dialect is read from your own schema and you choose the target.
Consolidate microservices comingMerge services that were split too far back into modules that hold together, keeping separate deployment only where it is worth it.

Test suites

OptionWhat it does
Test automation upliftMove a Selenium suite to a Playwright TypeScript project that runs. Locators are translated into provable equivalents rather than guessed, waits become automatic, and every original test is accounted for so coverage cannot quietly drop.
Selenium suite cleanup comingSteady the suite where it is: replace fixed sleeps with explicit conditions, put the constantly flaky tests aside, and delete dead or duplicate cases.
Refactor to Page Object Model comingMove inline locators into page objects, replace brittle selectors with stable ids and roles, and remove duplicated flows.
Coverage expansion comingFind the user flows the suite never reaches, write cases for them, and show what moved from untested to tested.
Generate test data in progressTurn your declared data model — types, constraints, foreign keys and routine parameters — into datasets you can commit, at whatever level of coverage you choose.

Platform and operations

OptionWhat it does
Go cloud-native comingContainerize, move configuration and secrets out of the code, add health probes and graceful shutdown, make the app stateless so it can scale sideways, then generate the deployment manifests.
Go event-driven comingPut a broker between services that talk to each other directly, with the outbox pattern, consumers that can safely retry, and a dead-letter queue.
Add distributed tracing comingInstrument the application with OpenTelemetry: traces that cross service boundaries, the standard metrics, and exemplars. "It is slow somewhere" becomes a flame graph.
Upgrade to structured logs comingReplace ad-hoc printing with structured, levelled logs that carry trace and request ids, hide secrets, and go somewhere you can query.
Cut cloud cost comingMeasure the real workload, set the right resource requests and limits, add autoscaling, slim the image, and give you a before-and-after estimate.
Move services to serverless comingFind the services that spike and then sit idle, and re-platform them so they scale to zero.
Make it AI-ready comingTurn the application into something an agent can operate: an MCP server over its domain and APIs, a retrieval store over the code, docs and data model, and a defined set of tools an assistant is allowed to call.

Moving to another language

A rewrite is its own thing, not a modernization option, because it produces a new codebase instead of changing the one you have. The original is left alone, and what the new system is built from is the structure the analysis recovered, not a model's reading of the old source.

From the mainframe

OptionWhat it does
COBOL to modern JavaPrograms, copybooks, job control and screens mapped onto a modern layered service, with the parsed structure as the source of truth.
COBOL to C# / .NETThe same path, targeting modern .NET.
COBOL to Python comingThe same path, targeting modern Python.
COBOL to Go comingThe same path, targeting Go, for small static binaries and batch throughput.
RPG (IBM i) to Java comingAS/400-era RPG programs, with display files, subfiles and DB2 access mapped onto a modern layered service.
PL/I to Java comingProcedures, includes and data structures recovered by the parser and rebuilt on a modern stack.

From older application stacks

OptionWhat it does
Legacy Java to modern JavaA full rebuild of the application, starting from the structure the analysis recovered.
Legacy Java to Kotlin comingThe same, in idiomatic Kotlin, with null safety and coroutines on the modern JVM.
VB6 to modern .NET comingForms, modules and COM dependencies listed first, then rebuilt as a desktop or web application you can maintain.
Delphi to modern .NET comingUnits, forms and data modules listed and rebuilt.
Perl to Python comingScripts and their dependencies listed, and the behaviour pinned down before anything is converted.

From systems languages

OptionWhat it does
C to Rust · Go · modern Java comingRebuild the C system, working from the structure recovered from the source. Rust for memory safety without losing the systems shape, Go for simple concurrency and static binaries, Java for a layered service.
C++ to Rust · Go · modern Java comingThe same three targets from C++, with ownership replacing manual lifetime handling where the target is Rust.

From older front ends

OptionWhat it does
AngularJS to ReactRebuild the 1.x application as modern React, component by component, working from the routes and controllers the analysis found.
jQuery front end to React comingPages and behaviours recovered from the DOM-manipulation code, and rebuilt with state where the selectors used to be.

Rewrite or upgrade

Where both exist for the same stack they answer different questions. An upgrade keeps your codebase and moves it forward: same modules, same behaviour, a diff you can review. A rewrite gives you a new codebase next to the old one. Java has both, and the wording on each option says which is which.