Java Upgrade
GitHubOfficialFirst committed by jduboisDistributed by GitHub
- Skill
- Guidelines
- Software development
- MIT
- Low risk
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides a structured approach to upgrading Java applications across LTS versions (11 to 17, 17 to 21, 21 to 25). It helps developers assess their current environment, update build configurations, and incrementally adopt modern Java features like records, sealed classes, pattern matching, and virtual threads. The skill emphasizes safety by recommending small, reviewable changes and provides specific migration patterns for each version transition.
What it does
- Detects current Java versions from build files like pom.xml and build.gradle.
- Provides version-specific upgrade guides for LTS transitions.
- Offers code migration patterns for modern Java features.
- Advises on updating build configurations and dependencies.
- Integrates with external tools like OpenRewrite for automated refactoring.
- Provides guidance on handling namespace migrations such as javax to jakarta.
When to use it
- When you need to upgrade a Java project to a newer JDK version.
- When you want to modernize existing Java code with newer language features.
- When you are unsure of the specific migration steps between two Java LTS versions.
- When you need to adopt features like records, sealed classes, or virtual threads.
Advantages
- Provides a clear, step-by-step path for complex version upgrades.
- Focuses on safe, incremental refactoring to minimize breaking changes.
- Includes specific code examples for modern Java syntax.
- Helps identify necessary build tool and dependency updates.
- Directs users to official documentation and migration resources.
Usage examples
- “How do I upgrade my Java 11 project to Java 17?”
- The agent will analyze your build files, outline the steps for the 11 to 17 transition, and provide specific migration patterns for features like records and sealed classes.
- “Show me how to use virtual threads in my Java 21 project.”
- The agent will explain the transition from platform threads to virtual threads using Executors.newVirtualThreadPerTaskExecutor and provide a code example.
- “How can I use pattern matching for switch in Java 21?”
- The agent will provide a comparison between traditional switch statements and the new pattern matching syntax, including examples with guarded patterns.
Install
Download the skill as a ZIP:
https://autogineer.com/agent-skills/github/java-upgrade/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip java-upgrade.zip -d .agents/skills
Documentation by jdubois
Java Upgrade
Help upgrade a Java project step by step and adopt new language features safely.
Need a more powerful, automated upgrade?
The Microsoft plugin modernize-java ("GitHub Copilot modernization - Java Upgrade CLI Plugin", also listed in this repository's plugins/external.json) upgrades Java applications from the command line. It can:
analyze the project, assess dependencies and generate a customizable upgrade plan you can review and edit;
execute the plan to automatically transform the codebase;
fix build issues and resolve migration errors during the upgrade, running test validations;
validate the application against known CVEs after the upgrade;
output a detailed summary (file changes, updated dependencies, test results, remaining issues) suitable for PRs or CI logs.
This skill provides lightweight, step-by-step guidance and works without the plugin; the plugin is the more powerful option for large or multi-step upgrades. It is a separate Microsoft plugin, not part of this repository's skills. To try it (see the plugin README for the recommended model):
npm install -g @github/copilot
copilot plugin marketplace add microsoft/modernize-java
copilot plugin install modernize-java@modernize-java
copilot --agent modernize-java:modernize-javaWorkflow
Detect the current and target versions. Read
pom.xml(maven.compiler.release,java.version, toolchains),build.gradle*(java.toolchain,sourceCompatibility) and CI files. Never assume a target: ask the user if it is not stated.Upgrade one LTS at a time (11 → 17 → 21 → 25). Build and run the tests after each step.
Load only the guide you need from the table below. Do not load all of them.
Update the build first (compiler release, plugins, dependencies that need newer versions), then adopt language features incrementally in small, reviewable changes.
Verify:
./mvnw verifyor./gradlew build, and fix deprecation and removal warnings before adopting new features.Automate and check upstream. Use OpenRewrite when available (for example
org.openrewrite.java.migrate.UpgradeToJava21) and always fetch the version-specific official guide before editing; review the generated diff.
Upstream sources (read before upgrading)
Fetch the page for the target version; prefer it over this skill when they conflict. Full list with build-tool and agent compatibility pages: references/upstream-sources.md.
OpenJDK release page: https://openjdk.org/projects/jdk/21/ (change the number) and JEP index https://openjdk.org/jeps/0
Oracle migration guide: https://docs.oracle.com/en/java/javase/21/migrate/getting-started.html
Oracle release notes: https://www.oracle.com/java/technologies/javase/21-relnote-issues.html
jdeprscan/jdeps: https://docs.oracle.com/en/java/javase/21/docs/specs/man/jdeprscan.html, https://docs.oracle.com/en/java/javase/21/docs/specs/man/jdeps.htmlGradle compatibility matrix: https://docs.gradle.org/current/userguide/compatibility.html
OpenRewrite: https://docs.openrewrite.org/recipes/java/migrate/upgradetojava21
Per-version guides
| Upgrade | Guide |
|---|---|
| Java 11 → 17 | references/java-11-to-17.md |
| Java 17 → 21 | references/java-17-to-21.md |
| Java 21 → 25 | references/java-21-to-25.md |
Rules
Prefer behavior-preserving refactorings; do not change public APIs unless asked.
Detect the project's frameworks (Spring Boot, Quarkus, Jakarta EE) and check their minimum supported Java version before raising the target.
Do not adopt preview features unless the user opts in and the build enables them explicitly.
For
javax.*tojakarta.*namespace changes, use thejavax-to-jakarta-migrationskill if it is installed (gh skills install github/awesome-copilot javax-to-jakarta-migration); otherwise do the migration inline: update imports,META-INFservice files and XML descriptors, and bump dependencies to Jakarta EE 9+ equivalents.If the
modernize-javaplugin is installed and the user wants an automated upgrade, defer to it and use this skill for the per-version guides and thejavaxtojakartasafeguards.
Files
- ATTRIBUTION.mdAdded by AutoGineer855 B
- LICENSELicence file added by AutoGineer1.0 KiB
- SKILL.md4.8 KiB
- references/java-11-to-17.md21.5 KiB
- references/java-17-to-21.md13.7 KiB
- references/java-21-to-25.md9.7 KiB
- references/upstream-sources.md2.3 KiB
references/java-17-to-21.md
Java 17 to Java 21 Upgrade Guide
These instructions help GitHub Copilot assist developers in upgrading Java projects from JDK 17 to JDK 21, focusing on new language features, API changes, and best practices.
Major Language Features in JDK 18-21
Pattern Matching for switch (JEP 441 - Standard in 21)
Enhanced switch Expressions and Statements
When working with switch constructs:
Suggest converting traditional switch to pattern matching where appropriate
Use pattern matching for type checking and destructuring
Example upgrade patterns:
// Old approach (Java 17)
public String processObject(Object obj) {
if (obj instanceof String) {
String s = (String) obj;
return s.toUpperCase();
} else if (obj instanceof Integer) {
Integer i = (Integer) obj;
return i.toString();
}
return "unknown";
}
// New approach (Java 21)
public String processObject(Object obj) {
return switch (obj) {
case String s -> s.toUpperCase();
case Integer i -> i.toString();
case null -> "null";
default -> "unknown";
};
}Support guarded patterns:
return switch (obj) {
case String s when s.length() > 10 -> "Long string: " + s;
case String s -> "Short string: " + s;
case Integer i when i > 100 -> "Large number: " + i;
case Integer i -> "Small number: " + i;
default -> "Other";
};Record Patterns (JEP 440 - Standard in 21)
Destructuring Records in Pattern Matching
When working with records:
Suggest using record patterns for destructuring
Combine with switch expressions for powerful data processing
Example usage:
public record Point(int x, int y) {}
public record ColoredPoint(Point point, Color color) {}
// Destructuring in switch
public String describe(Object obj) {
return switch (obj) {
case Point(var x, var y) -> "Point at (" + x + ", " + y + ")";
case ColoredPoint(Point(var x, var y), var color) ->
"Colored point at (" + x + ", " + y + ") in " + color;
default -> "Unknown shape";
};
}Use in complex pattern matching:
// Nested record patterns
return switch (shape) {
case Rectangle(ColoredPoint(Point(var x1, var y1), var c1),
ColoredPoint(Point(var x2, var y2), var c2))
when c1 == c2 -> "Monochrome rectangle";
case Rectangle r -> "Multi-colored rectangle";
default -> "Other shape";
};Virtual Threads (JEP 444 - Standard in 21)
Lightweight Concurrency
When working with concurrency:
Suggest Virtual Threads for high-throughput, concurrent applications
Use
Thread.ofVirtual()for creating virtual threadsExample migration patterns:
// Old platform thread approach
ExecutorService executor = Executors.newFixedThreadPool(100);
executor.submit(() -> {
// blocking I/O operation
httpClient.send(request);
});
// New virtual thread approach
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
executor.submit(() -> {
// blocking I/O operation - now scales to millions
httpClient.send(request);
});
}Use structured concurrency patterns:
// Structured concurrency (JEP 453 - Preview in 21, requires --enable-preview)
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
StructuredTaskScope.Subtask<String> user = scope.fork(() -> fetchUser(userId));
StructuredTaskScope.Subtask<String> order = scope.fork(() -> fetchOrder(orderId));
scope.join(); // Join all subtasks
scope.throwIfFailed(); // Propagate errors
return processResults(user.get(), order.get());
}String Templates (JEP 430 - Preview in 21)
Safe String Interpolation
When working with string formatting:
Suggest String Templates for safe string interpolation (preview feature)
Enable preview features with
--enable-previewExample usage:
// Traditional concatenation
String message = "Hello, " + name + "! You have " + count + " messages.";
// String Templates (Preview)
String message = STR."Hello, \{name}! You have \{count} messages.";
// FMT (java.util.FormatProcessor) adds format specifiers
// import static java.util.FormatProcessor.FMT;
String price = FMT."Total: %8.2f\{total}";Only STR, FMT and RAW are built in on JDK 21. Domain-specific processors (for example for HTML or SQL) must be written by the project as StringTemplate.Processor implementations; do not assume they exist.
Sequenced Collections (JEP 431 - Standard in 21)
Enhanced Collection Interfaces
When working with collections:
Use new
SequencedCollection,SequencedSet,SequencedMapinterfacesAccess first/last elements uniformly across collection types
Example usage:
// New methods available on Lists, Deques, LinkedHashSet, etc.
List<String> list = List.of("first", "middle", "last");
String first = list.getFirst(); // "first"
String last = list.getLast(); // "last"
List<String> reversed = list.reversed(); // ["last", "middle", "first"]
// Works with any SequencedCollection
SequencedSet<String> set = new LinkedHashSet<>();
set.addFirst("start");
set.addLast("end");
String firstElement = set.getFirst();Unnamed Patterns and Variables (JEP 443 - Preview in 21, requires --enable-preview)
Simplified Pattern Matching
When working with pattern matching:
Use unnamed patterns
_for values you don't needSimplify switch expressions and record patterns
Example usage:
// Ignore unused variables
switch (ball) {
case RedBall(_) -> "Red ball"; // Don't care about size
case BlueBall(var size) -> "Blue ball size " + size;
}
// Ignore parts of records
switch (point) {
case Point(var x, _) -> "X coordinate: " + x; // Ignore Y
case ColoredPoint(Point(_, var y), _) -> "Y coordinate: " + y;
}
// Exception handling with unnamed variables
try {
riskyOperation();
} catch (IOException | SQLException _) {
// Don't need exception details
handleError();
}Scoped Values (JEP 446 - Preview in 21, requires --enable-preview)
Improved Context Propagation
When working with thread-local data:
Consider Scoped Values as a modern alternative to ThreadLocal
Better performance and clearer semantics for virtual threads
Example usage:
// Define scoped value
private static final ScopedValue<String> USER_ID = ScopedValue.newInstance();
// Set and use scoped value
ScopedValue.where(USER_ID, "user123")
.run(() -> {
processRequest(); // Can access USER_ID.get() anywhere in call chain
});
// In nested method
public void processRequest() {
String userId = USER_ID.get(); // "user123"
// Process with user context
}API Enhancements and New Features
UTF-8 by Default (JEP 400 - Standard in 18)
When working with file I/O:
UTF-8 is now the default charset on all platforms
Remove explicit charset specifications where UTF-8 was intended
Example simplification:
// Old explicit UTF-8 specification
Files.readString(path, StandardCharsets.UTF_8);
Files.writeString(path, content, StandardCharsets.UTF_8);
// New default behavior (Java 18+)
Files.readString(path); // Uses UTF-8 by default
Files.writeString(path, content); // Uses UTF-8 by defaultSimple Web Server (JEP 408 - Standard in 18)
When needing basic HTTP server:
Use built-in
jwebservercommand or thecom.sun.net.httpserver.SimpleFileServerAPIGreat for testing and development
Example usage:
// Command line
$ jwebserver -p 8080 -d /path/to/files
// Programmatic usage
HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);
server.createContext("/", SimpleFileServer.createFileHandler(Path.of("/tmp")));
server.start();Internet-Address Resolution SPI (JEP 418 - Standard in 19)
When working with custom DNS resolution:
Implement
InetAddressResolverProviderfor custom address resolutionUseful for service discovery and testing scenarios
Key Encapsulation Mechanism API (JEP 452 - Standard in 21)
When working with key encapsulation:
Use KEM API for key encapsulation mechanisms
JDK 21 ships the DHKEM algorithm (RFC 9180); post-quantum ML-KEM only arrived in Java 24 (JEP 496)
Example usage:
KeyPairGenerator kpg = KeyPairGenerator.getInstance("X25519");
KeyPair kp = kpg.generateKeyPair();
KEM kem = KEM.getInstance("DHKEM");
KEM.Encapsulator encapsulator = kem.newEncapsulator(kp.getPublic());
KEM.Encapsulated encapsulated = encapsulator.encapsulate();Deprecations and Warnings
Finalization Deprecation (JEP 421 - Deprecated in 18)
When encountering finalize() methods:
Remove finalize methods and use alternatives
Suggest Cleaner API or try-with-resources
Example migration:
// Deprecated finalize approach
@Override
protected void finalize() throws Throwable {
cleanup();
}
// Modern approach with Cleaner
private static final Cleaner CLEANER = Cleaner.create();
public MyResource() {
CLEANER.register(this, new CleanupTask(nativeResource));
}
private static class CleanupTask implements Runnable {
private final long nativeResource;
CleanupTask(long nativeResource) {
this.nativeResource = nativeResource;
}
public void run() {
cleanup(nativeResource);
}
}Dynamic Agent Loading (JEP 451 - Warnings in 21)
When working with agents or instrumentation:
Add
-XX:+EnableDynamicAgentLoadingto suppress warnings if neededConsider loading agents at startup instead of dynamically
Update tooling to use startup agent loading
Build Configuration Updates
Preview Features
For projects using preview features:
Add
--enable-previewto compiler and runtimeMaven configuration:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<release>21</release>
<compilerArgs>
<arg>--enable-preview</arg>
</compilerArgs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>--enable-preview</argLine>
</configuration>
</plugin>Gradle configuration:
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
tasks.withType<JavaCompile> {
options.compilerArgs.add("--enable-preview")
}
tasks.withType<Test> {
jvmArgs("--enable-preview")
}Virtual Threads Configuration
For applications using Virtual Threads:
No special JVM flags required (standard feature in 21)
Consider these system properties for debugging:
-Djdk.virtualThreadScheduler.parallelism=N # Set carrier thread count
-Djdk.virtualThreadScheduler.maxPoolSize=N # Set max pool sizeRuntime and GC Improvements
Generational ZGC (JEP 439 - Available in 21)
When configuring garbage collection:
Try Generational ZGC for better performance
Enable with:
-XX:+UseZGC -XX:+ZGenerationalMonitor allocation patterns and GC behavior
Migration Strategy
Step-by-Step Upgrade Process
Update Build Tools: Ensure Maven/Gradle supports JDK 21
Language Feature Adoption:
Start with pattern matching for switch (standard)
Add record patterns where beneficial
Consider Virtual Threads for I/O heavy applications
Preview Features: Enable only if needed for specific use cases
Testing: Comprehensive testing especially for concurrency changes
Performance: Benchmark with new GC options
Code Review Checklist
When reviewing code for Java 21 upgrade:
[ ] Convert appropriate instanceof chains to switch expressions
[ ] Use record patterns for data destructuring
[ ] Replace ThreadLocal with ScopedValues where appropriate (preview in 21)
[ ] Consider Virtual Threads for high-concurrency scenarios
[ ] Remove explicit UTF-8 charset specifications
[ ] Replace finalize() methods with Cleaner or try-with-resources
[ ] Use SequencedCollection methods for first/last access patterns
[ ] Add preview flags only for preview features in use
Common Migration Patterns
Switch Enhancement:
// From instanceof chains to switch expressions if (obj instanceof String s) return processString(s); else if (obj instanceof Integer i) return processInt(i); // becomes: return switch (obj) { case String s -> processString(s); case Integer i -> processInt(i); default -> processDefault(obj); };Virtual Thread Adoption:
// From platform threads to virtual threads Executors.newFixedThreadPool(200) // becomes: Executors.newVirtualThreadPerTaskExecutor()Record Pattern Usage:
// From manual destructuring to record patterns if (point instanceof Point p) { int x = p.x(); int y = p.y(); } // becomes: if (point instanceof Point(var x, var y)) { // use x and y directly }
Performance Considerations
Virtual Threads excel with blocking I/O but may not benefit CPU-intensive tasks
Generational ZGC can reduce GC overhead for most applications
Pattern matching in switch is generally more efficient than instanceof chains
SequencedCollection methods provide O(1) access to first/last elements
Scoped Values have lower overhead than ThreadLocal for virtual threads
Testing Recommendations
Test Virtual Thread applications under high concurrency
Verify pattern matching covers all expected cases
Performance test with Generational ZGC vs other collectors
Validate UTF-8 default behavior across different platforms
Test preview features thoroughly before production use
Remember to enable preview features only when specifically needed and test thoroughly in staging environments before deploying to production.
Requirements and compatibility
Written for: GitHub Copilot
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Needs Node.js
Security
What our scanner noticed· Low risk
- Instructions that use the network
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- First committed by
jdubois
- Publisher
- GitHub (Official)
- Distributed by
- GitHub
- Source
- github/awesome-copilot · skills/java-upgrade · commit 797337f
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
- Contributors
- jdubois
AutoGineer is not affiliated with or endorsed by jdubois or GitHub.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download java-upgrade.zip