Hands-On Lab · App Modernization

I Modernized a Legacy Java App with AWS Transform—Here’s What Happened

Java 8 → Java 21. 18 files. 528 lines changed. 422 hours saved. One agentic CLI command.

📅 March 26, 2026 ⏱ 12 min read ☁️ AWS · Java · Modernization · Agentic AI
☰ In this article
  1. What is AWS Transform?
  2. Key Features & Concepts
  3. Setting Up AWS Transform
  4. The App: Spring Boot Petclinic on Java 8
  5. Creating a Workspace & Starting a Job
  6. Running the Transformation
  7. The Results: What the Agent Actually Did
  8. The App Runs on Java 21
  9. What’s Next: Part 2
  10. Key Takeaways

Legacy Java apps don’t age gracefully. Java 8 reached end of open-source support in 2019, Spring Boot 2.x followed in November 2023, and yet countless production codebases are still running on them—because upgrading manually is painful, risky, and time-consuming. AWS Transform promises to automate exactly this through agentic AI. I decided to put it to the test on a real Spring Boot petclinic app and document everything: setup, the first error I hit, what the agent changed, and whether the app actually ran afterward.

Spoiler: it worked. The agent made 528 line changes across 18 files, the dashboard showed 100% validation rate, and the petclinic came up clean on Java 21. Here’s the full story.

What is AWS Transform?

AWS Transform is AWS’s platform for enterprise IT modernization powered by agentic AI. It handles mainframe refactoring, VMware migration, Windows modernization, and—the focus here—Custom workloads like Java, Python, and Node.js runtime upgrades.

AWS Transform landing page —
→ AWS Transform landing page — "Agentic modernization of mainframe, VMware, Windows, and custom applications." The blue banner announces the new CLI feature.

The platform learns from every transformation, and the numbers on their workspace dashboard reflect the scale of enterprise adoption:

1.35M+Hours of manual effort saved
2.66B+Lines of code analyzed
80xFaster than manual migration
AWS Transform workspace dashboard — 1.35M+ hours saved (648 developer-years), 2.66B+ lines analyzed. My freshly created workspace-tW9nbGaP appears on the right with 0 jobs.
→ AWS Transform workspace dashboard — 1.35M+ hours saved (648 developer-years), 2.66B+ lines analyzed. My freshly created workspace-tW9nbGaP appears on the right with 0 jobs.

Key Features & Concepts

Before diving in, here are the core building blocks of AWS Transform:

🌐 Workspaces

Containers where you create jobs, store artifacts, and collaborate. Think of them as project folders with an AI agent as the project manager.

🤖 Agentic Job Plans

Multi-step workflows where the agent collects requirements through chat, then hands you a CLI command to execute locally against your codebase.

🔗 Connectors

Outgoing and incoming connectors for cross-account resource access. Useful for multi-account enterprise setups.

🤝 Partner Agents

Third-party transformation agents that plug into your workspace. They get deep access to artifacts and job data, so review security docs before enabling.

📄 Transformation Definitions

AWS-managed recipes for specific upgrade types. AWS/java-version-upgrade handles JDK bump, Jakarta EE migration, database drivers, and Spring ecosystem updates.

💻 CLI (atx)

Runs transformations locally against your codebase. Supports Custom workloads only — Java, Python, Node.js. The actual code changes happen on your machine.

Connectors page — cross-account resource sharing for enterprise setups. None needed for this single-account demo.
→ Connectors page — cross-account resource sharing for enterprise setups. None needed for this single-account demo.
Partner agents page — third-party agents with deep workspace access. Enable selectively and review security documentation first.
→ Partner agents page — third-party agents with deep workspace access. Enable selectively and review security documentation first.

Setting Up AWS Transform

AWS Transform is enabled from inside the AWS console. You need two things: the web application (the chat-based agentic UI) and the CLI (for executing transformations locally).

Step 1 — Enable Capabilities

Navigate to AWS Transform and click Get Started. The capabilities page shows two sections: CLI for Custom workloads, and Web application for the full scope including mainframe and VMware.

Enable AWS Transform capabilities — CLI covers Custom workloads (Java, Python, Node.js). Web application covers mainframe, VMware, and Windows workloads too.
→ Enable AWS Transform capabilities — CLI covers Custom workloads (Java, Python, Node.js). Web application covers mainframe, VMware, and Windows workloads too.

⚠️ Note: The CLI supports only Custom workloads. For mainframe, VMware, and Windows, you need the web application.

Step 2 — Choose Your Identity Provider

Enabling the web app requires picking an identity provider: AWS Identity Center or an existing IdP (Microsoft Entra or Okta). This is a one-time irreversible decision.

Enable AWS Transform web application — choose AWS Identity Center (IdC) or an existing IdP. Cannot be changed after enabling. IAM Identity Center must be set up first if using IdC.
→ Enable AWS Transform web application — choose AWS Identity Center (IdC) or an existing IdP. Cannot be changed after enabling. IAM Identity Center must be set up first if using IdC.

🚨 One-time decision: You cannot change the identity provider type after enabling. Pick the right one for your org before clicking Enable.

Step 3 — Setup Complete

After enabling, the success screen confirms the web application is live and a service profile has been created.

Setup complete — AWS Transform web application enabled, service profile created. Ready to manage users and create workspaces.
→ Setup complete — AWS Transform web application enabled, service profile created. Ready to manage users and create workspaces.

Step 4 — Install the CLI

curl -fsSL https://desktop-release.transform.us-east-1.api.aws/install.sh | bash

Requirements: Node 20+ must be installed. Does not run natively on Windows (use WSL). Configure AWS credentials with required IAM policies before running any atx commands.

The App: Spring Boot Petclinic on Java 8

I needed a realistic legacy app — not a toy. The Spring Boot petclinic is a well-known demo with real dependencies: Spring Boot 2.7.3, pinned to Java 1.8, with a full stack of libraries that all need updating. Here’s the pom.xml:

Terminal — cat pom.xml showing spring-petclinic version 2.7.3, spring-boot-starter-parent 2.7.3, java.version 1.8. Spring Boot 2.7.x reached end of support November 2023.
→ Terminal — cat pom.xml showing spring-petclinic version 2.7.3, spring-boot-starter-parent 2.7.3, java.version 1.8. Spring Boot 2.7.x reached end of support November 2023.
<artifactId>spring-petclinic</artifactId>
<version>2.7.3</version>

<parent>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.7.3</version>
</parent>

<java.version>1.8</java.version>

This is a textbook modernization target. Upgrading manually means: bumping the Java version, migrating every javax.* import to jakarta.*, updating database drivers, upgrading Spring Boot to a 3.x line, and resolving cascading dependency conflicts. AWS Transform is supposed to handle all of that autonomously.

Creating a Workspace & Starting a Job

Create a Workspace & Job

In the AWS Transform console, create a workspace, then inside it create a new CustomTransformation job. You’re dropped into the agentic chat interface with a two-step job plan in the sidebar:

The CustomTransformation job interface — Job plan on the left with
→ The CustomTransformation job interface — Job plan on the left with "Select transformation definition" (In-progress) and "Monitor transformation progress". The agent opens: "What would you like to upgrade?"

Chat with the Agent

I typed exactly what I wanted: "Upgrade the Java 8 Spring Boot application to Java 21."

The agent responds immediately: it found AWS/java-version-upgrade — covering Jakarta EE migration, database drivers, ORM frameworks, and Spring ecosystem updates.
→ The agent responds immediately: it found AWS/java-version-upgrade — covering Jakarta EE migration, database drivers, ORM frameworks, and Spring ecosystem updates. "Would you like to proceed?"

The agent identified AWS/java-version-upgrade — an AWS-managed transformation definition that handles the full stack: JDK bump, Jakarta EE namespace migration, database drivers, ORM frameworks, and Spring ecosystem updates. I said yes. The agent then generated the CLI command and opened the Monitor panel:

After approval — the agent confirms the plan and opens the
→ After approval — the agent confirms the plan and opens the "Monitor transformation progress" panel with the full atx CLI command pre-filled, including the campaign ID. Step 1: install CLI. Step 2: execute.

🤖 The web UI has done its job: collected intent, matched a transformation definition, generated a ready-to-run command. Now it moves to the terminal.

Running the Transformation — And Hitting an Error First

I copied the command from the dashboard and ran it. First attempt: I forgot the --configuration flag.

First attempt — Error: non-interactive mode requires --configuration with additionalPlanContext populated. The CLI needs the target Java version specified upfront since it cannot pause to ask in headless mode.
→ First attempt — Error: non-interactive mode requires --configuration with additionalPlanContext populated. The CLI needs the target Java version specified upfront since it cannot pause to ask in headless mode.

Error: Non-interactive mode requires --configuration with additionalPlanContext — the target language version must be provided before the agent runs headlessly.

Three ways to pass the config:

# Inline flag (what I used)
--configuration "additionalPlanContext=The target Java version to upgrade to is Java 21"

# JSON file
--configuration "file://path/to/config.json"
# { "additionalPlanContext": "The target Java version to upgrade to is Java 21" }

# YAML file
# additionalPlanContext: The target Java version to upgrade to is Java 21

With the flag added, the second attempt ran cleanly:

Second attempt — full command with --configuration flag. The CLI confirms AWS/java-version-upgrade, marks the repo as in-progress, shows
→ Second attempt — full command with --configuration flag. The CLI confirms AWS/java-version-upgrade, marks the repo as in-progress, shows "You are using an AWS Managed transformation." then enters Thinking... state.

The full working command:

atx custom def exec \
  --code-repository-path ~/petclinic-legacy-demo \
  --non-interactive \
  --trust-all-tools \
  --campaign ced189bc-bafb-4465-8d67-bda247aaf391 \
  --repo-name petclinic-legacy-demo \
  --add-repo \
  --configuration "additionalPlanContext=The target Java version to upgrade to is Java 21"

--trust-all-tools skips per-tool confirmation prompts. Combined with --non-interactive, the agent runs fully autonomously. "Thinking..." means it’s reading the codebase, scanning imports, mapping dependencies.

Under the Hood: What the Agent Does While "Thinking..."

The "Thinking..." state isn’t idle. The agent immediately starts producing artifacts. First it creates a plan.json — a structured JSON file that maps out every step, its goal, target components, and the exact verification command to run after:

Agent creates plan.json — a structured transformation plan with step titles, goals, descriptions, target components (pom.xml, .mvn/wrapper), and verification commands using JAVA_HOME to compile with the correct JDK.
→ Agent creates plan.json — a structured transformation plan with step titles, goals, descriptions, target components (pom.xml, .mvn/wrapper), and verification commands using JAVA_HOME to compile with the correct JDK.

It then creates a Transformation Worklog — a running log file tracking the project and every action taken:

Agent creates the Transformation Worklog — logs the project as
→ Agent creates the Transformation Worklog — logs the project as "spring-petclinic (Java 8 → Java 21, Spring Boot)" and begins recording each operation for traceability.

With the plan established, the agent starts making targeted edits. Here it is using the editor tool (trusted) to make precise str_replace operations on pom.xml — each one surgical, each one completing in milliseconds:

Agent making pom.xml edits — three str_replace operations: java.version 1.8→21, jacoco-maven-plugin 0.8.7→0.8.12, spring-javaformat-maven-plugin 0.0.31→0.0.41. Each completes in 0–1ms.
→ Agent making pom.xml edits — three str_replace operations: java.version 1.8→21, jacoco-maven-plugin 0.8.7→0.8.12, spring-javaformat-maven-plugin 0.0.31→0.0.41. Each completes in 0–1ms.

After each step’s changes pass the build verification, the agent uses the vcs_control tool to commit directly to the staging branch with a structured commit message:

Agent uses vcs_control (trusted) — Action: commit, Repository: /root/petclinic-legacy-demo, Branch: atx-result-staging-20260326_123016_546f6e61, Commit Message:
→ Agent uses vcs_control (trusted) — Action: commit, Repository: /root/petclinic-legacy-demo, Branch: atx-result-staging-20260326_123016_546f6e61, Commit Message: "Step 1: Establish Build Baseline with Source Java Version Build status: Success". Completed in 82ms.

🔎 Full transparency: Every tool call is logged — editor, vcs_control, build runner. You can see exactly what the agent touched and why, file by file and line by line.

The Results: What the Agent Actually Did

The agent didn’t just bump a version number. It worked through the codebase in six structured, independently-committed steps — each one passing its own build validation before the next began.

Estimated Time Saved: 422 Hours

The transformation dashboard calculated 422 hours of manual effort saved, at a baseline of 10 lines of code per developer per day. The dashboard does not update automatically — you ask the agent in chat to refresh it.

AWS Transform Dashboard — Estimated time saved: 422 hours, updated at 16:28:50 UTC. Uses a baseline of 10 LoC/day. You can customize this rate in chat by providing your team's actual productivity figure.
→ AWS Transform Dashboard — Estimated time saved: 422 hours, updated at 16:28:50 UTC. Uses a baseline of 10 LoC/day. You can customize this rate in chat by providing your team's actual productivity figure.

100% Validated — 528 Lines, 18 Files

Transformation progress — Validated 1 unit, 100%. Repository petclinic-legacy-demo: 18 files changed, 528 lines of code modified, Validation rate 100%.
→ Transformation progress — Validated 1 unit, 100%. Repository petclinic-legacy-demo: 18 files changed, 528 lines of code modified, Validation rate 100%.
528Lines of code modified
18Files changed
100%Validation rate

The Full Transformation Summary

At the end of the run, the agent outputs a complete Transformation Summary — every version change, every library migrated, and the final test results:

Transformation Summary — Source: Java 8, Spring Boot 2.7.3, javax.*. Target: Java 21, Spring Boot 3.2.12, jakarta.*. Full changelog of every version bump, plugin change, and namespace migration. Test Results: 41 tests, 0 failures, 0 errors, 1 skipped.
→ Transformation Summary — Source: Java 8, Spring Boot 2.7.3, javax.*. Target: Java 21, Spring Boot 3.2.12, jakarta.*. Full changelog of every version bump, plugin change, and namespace migration. Test Results: 41 tests, 0 failures, 0 errors, 1 skipped.

The Six-Step Commit History

Every step was committed separately by ATX Bot to the atx-result-staging branch, with a build verification before each commit:

GitHub — atx-result-staging branch showing 6 commits by ATX Bot on March 26, 2026. Each step is labeled with its purpose and ends with
→ GitHub — atx-result-staging branch showing 6 commits by ATX Bot on March 26, 2026. Each step is labeled with its purpose and ends with "Build status: Success".
1
Step 1: Establish Build Baseline with Source Java Version
ATX Bot · 441f15e · ✅ Build status: Success
2
Step 2: Update Build Tools and Maven Compiler Plugin for Java 21 Compatibility
ATX Bot · 732e042 · ✅ Build status: Success
3
Step 3: Jakarta EE Migration — Update Dependencies and Migrate All javax.* Imports to jakarta.* Atomically
ATX Bot · f7a51fc · ✅ Build status: Success
4
Step 4: Update Database Drivers (H2, MySQL, PostgreSQL)
ATX Bot · 94df524 · ✅ Build status: Success
5
Step 5: Upgrade Spring Boot to 3.2.12 and Update Spring Ecosystem Dependencies
ATX Bot · c534920 · ✅ Build status: Success
6
Step 6: Final Integration Verification and Cleanup
ATX Bot · 1bbbde7 · ✅ Build status: Success

🤖 Each step validates the build before committing and moving on. If step 3 had broken the build, the agent would have stopped and reported back rather than silently proceeding to step 4.

What Changed Across Those 18 Files

💾 pom.xml

Java version bumped to 21. Spring Boot parent updated from 2.7.3 → 3.2.12. Maven compiler plugin updated. All dependency versions reconciled.

🔄 javax → jakarta

Every javax.* import across all source files migrated to jakarta.* atomically in a single step. This alone touches dozens of lines across multiple classes.

🖤 Database Drivers

H2, MySQL, and PostgreSQL driver dependencies updated to versions compatible with Jakarta EE and Spring Boot 3.x.

🧩 Spring Ecosystem

Spring Boot starters, Spring Data, Spring MVC — updated as a coordinated set, not individually, avoiding version conflict cascades.

The App Runs on Java 21

The proof that matters: does the app actually work after the transformation? I deployed the transformed code to an EC2 instance and hit it in a browser.

Spring Boot petclinic running live at 18.234.250.46:8080 — Welcome page loads, navigation intact (Home, Find Owners, Veterinarians, Error), Spring logo visible. This is the transformed codebase on Java 21 and Spring Boot 3.2.12.
→ Spring Boot petclinic running live at 18.234.250.46:8080 — Welcome page loads, navigation intact (Home, Find Owners, Veterinarians, Error), Spring logo visible. This is the transformed codebase on Java 21 and Spring Boot 3.2.12.

🎉 It works. Java 8 Spring Boot 2.7.3 → Java 21 Spring Boot 3.2.12, running in production on EC2. Zero manual code edits required.

What’s Next — Part 2: Pentesting the Modernized App

The app is live on Java 21. But a modernized app isn’t necessarily a secure app. In Part 2, I’ll walk through how I pentested this exact petclinic deployment using the AWS Security Agent — running vulnerability scans, probing the attack surface, and seeing what AWS’s AI-powered security tooling finds on a freshly modernized Spring Boot application.

🔐 Coming in Part 2: Pentesting the modernized petclinic with AWS Security Agent — what vulnerabilities surface, how the agent reports them, and what a real security posture looks like post-transformation.

Key Takeaways

⚡ Setup is fast

From zero to a running transformation took under 30 minutes including IAM Identity Center setup and CLI installation.

🤖 The agent is genuinely capable

It identified the right transformation definition without prompting, worked through 6 structured steps, validated each build, and produced a working application.

📝 Don’t forget --configuration

In non-interactive mode, --configuration "additionalPlanContext=..." is required. Easy to miss when copying the base command from the dashboard.

🔎 Incremental commits are key

Six separate commits, each with a passing build, means you can review exactly what changed at each stage and roll back to any specific step.

🌟 It goes well beyond pom.xml

The javax→jakarta migration alone touched multiple source files. The agent understood semantic meaning, not just find-and-replace.

⏱ 422 hours is real

Even at a faster LoC/day rate, the coordination cost of a Java 8→21 migration is substantial. The agent handled it in one command.

AWS Transform Java 8 to 21 Spring Boot 3 Jakarta EE App Modernization Agentic AI atx CLI Legacy Java