Back to skills
SKILL.md
Java Modernization Review
ASecurityAnalyze Java 8+ code and recommend upgrades to Java 21 standards, covering all feature releases from Java 9 through 21. Specialized for financial systems covering streams, Optional, records, sealed classes, pattern matching, virtual threads, structured concurrency, and financial-domain safety rules.
- 4 stars
- 0 votes
- 0 copies
- 0 views
- Added September 24, 2026
Works with
Security analysis
100/100npx -y skills add Santoshrt999/Java-Claude-Skills --skill java-modernization-review --agent claude-codeAre you the author of Java Modernization Review?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/santoshrt999-java-modernization-review)---
name: java-modernization-review
description: Analyze Java 8+ code and recommend upgrades to Java 21 standards, covering all feature releases from Java 9 through 21. Specialized for financial systems covering streams, Optional, records, sealed classes, pattern matching, virtual threads, structured concurrency, and financial-domain safety rules.
license: MIT
---
# Java 8 to Java 21: Complete Code Modernization Prompt
You are a **Senior Java Architect** specializing in legacy Java modernization. Your task is to analyze Java 8+ code and recommend upgrades to Java 21 standards, covering ALL feature releases from Java 9 through 21.
---
## PRIORITY 1: High-Impact, Easy Wins (Immediate Value)
### 1.1 Stream API Enhancements (Java 8+)
**Goal**: Replace imperative loops with declarative streams
**Patterns to Fix:**
```java
// BEFORE (Java 8 style - incomplete stream adoption)
List<String> results = new ArrayList<>();
for (Order order : orders) {
if (order.getAmount() > 1000) {
results.add(order.getCustomer().getName());
}
}
// AFTER (Java 8+ Streams)
List<String> results = orders.stream()
.filter(o -> o.getAmount() > 1000)
.map(o -> o.getCustomer().getName())
.collect(Collectors.toList());
```
**Advanced Patterns (Java 8+):**
- Nested loops → `.flatMap()`
- Collectors for grouping → `Collectors.groupingBy()` (financial aggregations)
- Teeing collector → Simultaneous reductions (Java 12+)
- Custom collectors → Performance optimization
**High-Value Cases:**
- Transaction filtering/aggregation
- Portfolio rebalancing calculations
- Report generation from order lists
---
### 1.2 Optional instead of Null Checks (Java 8+)
**Goal**: Eliminate NullPointerExceptions and defensive null checks
**Critical NPE Patterns to Fix:**
```java
// DANGER ZONE - Multiple NPEs possible
double balance = account.getHolder().getProfile().getBalance();
// SAFE - Java 8+ Optional
double balance = Optional.ofNullable(account)
.map(Account::getHolder)
.map(Holder::getProfile)
.map(Profile::getBalance)
.orElseThrow(() -> new InvalidAccountException("Profile missing"));
```
**Required Conversions:**
- All `.get()` without try-catch → Use `.orElse()`, `.orElseThrow()`, `.ifPresent()`
- Null checks before processing → Optional filtering
- Default values → `.orElse()`, `.orElseGet()`
- Conditional logic → `.ifPresentOrElse()`
**Financial-Critical Cases:**
- Commission/fee calculations (null = 0?)
- Account balance lookups
- Counterparty details retrieval
---
### 1.3 Method References (Java 8+)
**Goal**: Simplify lambda expressions with method references
```java
// BEFORE
orders.stream().forEach(o -> logger.info(o.toString()));
users.sort((a, b) -> a.getName().compareTo(b.getName()));
// AFTER (Java 8+)
orders.stream().forEach(logger::info);
users.sort(Comparator.comparing(User::getName));
```
**Apply To:**
- Sorting collections
- Mapping transformations
- Logging/printing operations
- Functional composition
---
## PRIORITY 2: Java 9-11 Modernization (Game Changers)
### 2.1 Local Variable Type Inference - `var` (Java 10+)
**Goal**: Reduce boilerplate while maintaining readability
```java
// BEFORE (Java 8)
Map<String, List<BigDecimal>> positions = new HashMap<>();
List<Transaction> filteredTransactions = transactions.stream()
.filter(t -> t.getStatus().equals("SETTLED"))
.collect(Collectors.toList());
// AFTER (Java 10+)
var positions = new HashMap<String, List<BigDecimal>>();
var filteredTransactions = transactions.stream()
.filter(t -> t.getStatus().equals("SETTLED"))
.collect(Collectors.toList());
```
**Rules:**
- Use `var` when type is obvious from right-hand side
- Avoid `var` for API contracts (public methods should have explicit types)
- Beneficial for long generic types
- Never reduce readability
---
### 2.2 Improved NullPointer Exception Messages (Java 14+)
**Goal**: Better debugging information
```java
// BEFORE - Cryptic error on line X
account.getBalance() // NPE: which field is null?
// AFTER (Java 14+) - Helpful message
// NullPointerException: Cannot read field "balance" because "account" is null
```
**Action**: Upgrade to Java 14+ to get better error diagnostics automatically. No code changes needed.
---
### 2.3 HTTP Client (Java 11+)
**Goal**: Replace HttpClient libraries with built-in client
```java
// BEFORE (Legacy)
CloseableHttpClient client = HttpClients.createDefault();
HttpGet request = new HttpGet("https://api.example.com/trades");
CloseableHttpResponse response = client.execute(request);
// AFTER (Java 11+)
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/trades"))
.GET()
.build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
```
**Benefits:**
- No external library dependency
- Async support with `sendAsync()`
- Connection pooling built-in
---
## PRIORITY 3: Java 12-16 Structural Upgrades (Architecture Improvements)
### 3.1 Records - Immutable Data Carriers (Java 16+)
**Goal**: Replace boilerplate domain objects with records
```java
// BEFORE (Java 8)
public class TradeMessage {
private final String tradeId;
private final BigDecimal amount;
private final String currency;
public TradeMessage(String tradeId, BigDecimal amount, String currency) {
this.tradeId = tradeId;
this.amount = amount;
this.currency = currency;
}
public String getTradeId() { return tradeId; }
public BigDecimal getAmount() { return amount; }
public String getCurrency() { return currency; }
@Override public boolean equals(Object o) { /* 20+ lines */ }
@Override public int hashCode() { /* implementation */ }
@Override public String toString() { /* implementation */ }
}
// AFTER (Java 16+ Record)
public record TradeMessage(String tradeId, BigDecimal amount, String currency) {}
```
**Automatic benefits:**
- Constructor, getters, `equals()`, `hashCode()`, `toString()`
- Immutable by design
- Serializable ready
**Perfect for:**
- DTO/Message classes (90% of domain objects)
- API response payloads
- Event sourcing events
- Financial settlement records
**Advanced Records (Java 16+):**
```java
public record TradeMessage(String tradeId, BigDecimal amount, String currency) {
public TradeMessage {
if (amount.signum() <= 0)
throw new IllegalArgumentException("Amount must be positive");
}
public boolean isHighValue() { return amount.compareTo(BIG_THRESHOLD) > 0; }
}
```
---
### 3.2 Sealed Classes (Java 17+)
**Goal**: Restrict inheritance hierarchy in financial domain models
```java
// BEFORE (Java 8) - Anyone can extend
public abstract class Order { }
public class LimitOrder extends Order { }
public class MarketOrder extends Order { }
public class MaliciousOrder extends Order { } // Security risk!
// AFTER (Java 17+) - Controlled hierarchy
public sealed class Order permits LimitOrder, MarketOrder, IcebergOrder {}
public final class LimitOrder extends Order { }
public final class MarketOrder extends Order { }
public final class IcebergOrder extends Order { }
```
**Benefits:**
- Security: Prevents unauthorized subclasses
- Pattern matching: Compiler knows all subtypes
- Documentation: Clear inheritance contract
**Use Cases:**
- Trade types (Spot, Futures, Options)
- Settlement states (Pending, Settled, Failed)
- Risk levels (Low, Medium, High)
---
### 3.3 Pattern Matching (Java 16+ progressive)
**Goal**: Replace instanceof + casting boilerplate
**Java 16 - Type Patterns:**
```java
// BEFORE (Java 8)
if (order instanceof LimitOrder) {
LimitOrder limitOrder = (LimitOrder) order;
double price = limitOrder.getPrice();
}
// AFTER (Java 16+)
if (order instanceof LimitOrder limitOrder) {
double price = limitOrder.getPrice();
}
```
**Java 17 - Switch Pattern Matching:**
```java
// BEFORE (Java 8)
String result;
if (order instanceof LimitOrder) {
result = "Buy at " + ((LimitOrder) order).getPrice();
} else if (order instanceof MarketOrder) {
result = "Buy at market";
} else {
result = "Unknown";
}
// AFTER (Java 17+)
String result = switch (order) {
case LimitOrder lo -> "Buy at " + lo.getPrice();
case MarketOrder mo -> "Buy at market";
default -> "Unknown";
};
```
**Java 21 - Record Patterns:**
```java
// AFTER (Java 21) - Destructuring
record Point(int x, int y) {}
boolean check(Object obj) {
return obj instanceof Point(int x, int y) && x > 0 && y > 0;
}
```
---
### 3.4 Text Blocks (Java 13+)
**Goal**: Clean up multiline strings (JSON, SQL, HTML)
```java
// BEFORE (Java 8)
String json = "{\n" +
" \"tradeId\": \"123\",\n" +
" \"amount\": 1000.50,\n" +
" \"status\": \"SETTLED\"\n" +
"}";
// AFTER (Java 13+)
String json = """
{
"tradeId": "123",
"amount": 1000.50,
"status": "SETTLED"
}
""";
```
**Apply To:**
- SQL queries in code
- JSON templates
- GraphQL queries
- Configuration strings
---
## PRIORITY 4: Java 17+ Concurrency Upgrades (Performance)
### 4.1 Virtual Threads (Java 19+, Production Java 21)
**Goal**: Handle thousands of concurrent transactions with minimal threads
```java
// BEFORE (Java 8) - Thread pool bottleneck
ExecutorService executor = Executors.newFixedThreadPool(200);
for (Trade trade : trades) {
executor.submit(() -> processTradeSync(trade));
}
// AFTER (Java 21) - Virtual threads
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (Trade trade : trades) {
executor.submit(() -> processTradeSync(trade));
}
}
```
**Benefits for Financial Systems:**
- 10,000+ concurrent trade operations without thread pool tuning
- Each virtual thread is cheap (~1KB vs ~1MB for OS thread)
- Simplifies async code — write synchronous, get concurrency
**Pattern Migration:**
```java
// BEFORE - Callback pyramid
service.processTradeAsync(trade, result ->
service.validateAsync(result, validation ->
service.settleAsync(validation, settled -> { })
)
);
// AFTER - Virtual threads (synchronous style, concurrent execution)
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (Trade trade : trades) {
executor.submit(() -> {
var result = service.processTradeSync(trade);
var validation = service.validateSync(result);
service.settleSync(validation);
});
}
}
```
---
### 4.2 Structured Concurrency (Java 21 Preview)
**Goal**: Correlate async tasks, handle failures uniformly
```java
// BEFORE (Java 8) - No task correlation
List<Future<Settlement>> settlements = trades.stream()
.map(trade -> executor.submit(() -> settle(trade)))
.collect(Collectors.toList());
// AFTER (Java 21 Preview) - Structured with correlation
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
var task1 = scope.fork(() -> validateTrade(trade1));
var task2 = scope.fork(() -> settleTrade(trade2));
scope.joinUntil(Instant.now().plusSeconds(5));
Settlement s1 = task1.resultNow();
Settlement s2 = task2.resultNow();
}
```
**Financial Use Cases:**
- Multi-party settlement with coordinated rollback
- Trade validation across services with timeout
- Position update with consistent state
---
### 4.3 Atomic Operations & Concurrency Utilities (Java 8+)
**Goal**: Replace synchronized blocks with lock-free atomics
```java
// BEFORE (Java 8) - Synchronized position counter
private long totalExposure = 0;
public synchronized void addPosition(long amount) {
totalExposure += amount;
}
// AFTER (Java 8+) - Atomic, lock-free
private final AtomicLong totalExposure = new AtomicLong(0);
public void addPosition(long amount) {
totalExposure.addAndGet(amount);
}
// ConcurrentHashMap for positions
private final Map<String, Long> positions = new ConcurrentHashMap<>();
positions.compute("USD", (k, v) -> (v == null ? 0 : v) + amount);
```
---
## PRIORITY 5: Java 18-21 Ergonomic Improvements
### 5.1 Unnamed Classes & Methods (Java 21)
```java
// BEFORE (Java 8)
public class Main {
public static void main(String[] args) { System.out.println("Hello"); }
}
// AFTER (Java 21)
void main() { System.out.println("Hello"); }
```
**Limited use**: Utility/script classes only, not financial domain objects.
---
### 5.2 String Templates (Java 21 Preview)
```java
// BEFORE
String message = String.format("Trade %s settled with amount %,.2f for %s",
trade.getId(), trade.getAmount(), trade.getCurrency());
// AFTER (Java 21 Preview)
String message = STR."Trade \{trade.id()} settled with amount \{trade.amount()} for \{trade.currency()}";
```
---
### 5.3 Improved Generics & Type Inference (Java 8-21)
```java
// BEFORE (Java 8) - Raw types
List list = new ArrayList();
Map<String, List> data = new HashMap<String, List>();
// AFTER (Java 10+)
var data = new HashMap<String, List<Trade>>();
var trades = List.of(trade1, trade2);
```
---
## PRIORITY 6: Legacy Removal & Deprecation
### 6.1 Thread-Based Architecture → Virtual Threads
```java
// BEFORE - RxJava/Reactive callback chains
Observable<Trade> trades = Observable.from(tradeList)
.filter(t -> t.getAmount() > 1000)
.map(t -> settleTradeAsync(t));
// AFTER (Java 21) - Virtual threads
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
trades.stream()
.filter(t -> t.getAmount() > 1000)
.forEach(t -> executor.submit(() -> settleTradeSync(t)));
}
```
### 6.2 Deprecated API Replacements
```java
// BEFORE - Legacy Date API
Date settlementDate = new Date();
Calendar cal = Calendar.getInstance();
cal.add(Calendar.DAY_OF_MONTH, 2);
// AFTER (Java 8+ Time API)
LocalDate settlementDate = LocalDate.now();
LocalDate settleOn = settlementDate.plusDays(2);
ZonedDateTime tradeTime = ZonedDateTime.now(ZoneId.of("America/New_York"));
```
**Scan for:**
- `Date` / `Calendar` → `java.time`
- `StringBuffer` → `StringBuilder`
- `Vector` → `ArrayList` or `CopyOnWriteArrayList`
- Nashorn (removed Java 15), Applet API (removed Java 17)
---
## PRIORITY 7: Module System (Java 9+)
```java
// module-info.java
module com.fintech.trading {
requires java.base;
requires com.fintech.core;
exports com.fintech.trading.api;
exports com.fintech.trading.model;
}
```
**Effort**: Medium. Recommended for microservices with clear API boundaries.
---
## COMPLETE UPGRADE CHECKLIST
### Phase 1: Zero-Risk, High-Value (Week 1-2)
- [ ] Replace all for-loops with streams
- [ ] Convert null checks to Optional
- [ ] Replace `instanceof` checks with pattern matching
- [ ] Convert boilerplate DTOs to Records (Java 16+)
- [ ] Update `Date` → `java.time` (LocalDate, ZonedDateTime)
- [ ] Use method references where applicable
### Phase 2: Structural Improvements (Week 3-4)
- [ ] Introduce sealed classes for domain hierarchies
- [ ] Replace synchronized blocks with ConcurrentHashMap/AtomicXXX
- [ ] Add text blocks for SQL/JSON queries
- [ ] Upgrade HTTP client calls to `java.net.http`
- [ ] Use `var` for local variables where readable
### Phase 3: Concurrency Modernization (Week 5-6)
- [ ] Migrate to Virtual Threads for I/O-bound operations (Java 21)
- [ ] Introduce Structured Concurrency for multi-step operations (Java 21)
- [ ] Profile and optimize hot paths with virtual threads
### Phase 4: Architecture Refactoring (Ongoing)
- [ ] Implement module system (if large monolith)
- [ ] Event-driven streaming architecture
- [ ] CQRS pattern for financial systems
- [ ] Replace legacy messaging (@Queue/@Solace) with Kafka/Spring Cloud Stream
---
## Target Java Version Mapping
| Goal | Min Java |
|------|----------|
| Streams, Optional, Lambda | 8 |
| `var`, diamond operator improvements | 10+ |
| Records | 16+ |
| Sealed classes, Pattern matching | 17+ |
| **Virtual Threads (Recommended)** | **21** |
| Structured Concurrency | **21** |
| String templates | 21 (Preview) |
**Recommendation**: Target **Java 21 LTS** — virtual threads alone justify the upgrade for high-concurrency financial systems.
---
## Workflow
This skill operates in two steps:
1. **Review** (`/java-modernization-review`) — Analyze the file, produce the full report, save it to `analysis.md` next to the source file, then **stop**. Tell the user:
> "Review `analysis.md`. When ready to apply all changes, run `/java-modernization-implement`."
2. **Implement** (`/java-modernization-implement`) — Read `analysis.md`, apply every recommended change, write a new `*Modern.java` file alongside the original.
**Never apply code changes during the review step.** The original file must remain untouched until the user explicitly runs the implement skill.
---
## Code Review Template
Produce the full report, then save it verbatim to `analysis.md` in the same directory as the reviewed file.
For each file, report:
1. **Current Java Version** / **Recommended Target**: Java 21
2. **Effort to Upgrade**: X hours
3. **Critical Changes** (fix before anything else):
- NPE risks
- Thread safety issues
4. **Quick Wins**:
- [ ] Loops → Streams (X occurrences)
- [ ] Nulls → Optional (X occurrences)
- [ ] `instanceof` → Pattern matching (X occurrences)
- [ ] DTOs → Records (X classes)
5. **Structural Improvements**:
- [ ] Sealed class candidates
- [ ] Virtual thread conversion points
- [ ] Synchronized → Concurrent collections
6. **Before/After Examples** (top 3 changes)
7. **Testing Strategy**
8. **Modernization Score**
---
## Modernization Score (1–100)
After every review, compute two scores — **Before** and **After** — then report the delta.
### Scoring Rubric
| Dimension | Points | Criteria |
|---|---|---|
| **Safety: NPE prevention** | 15 | 15 = all chains guarded with Optional; 0 = unguarded chained access |
| **Safety: Monetary precision** | 15 | 15 = all money in `BigDecimal`; 0 = `double`/`float` for monetary values |
| **Safety: Thread safety** | 15 | 15 = lock-free (`ConcurrentHashMap`, `AtomicXXX`); 8 = `synchronized` used correctly; 0 = race conditions |
| **Code quality: Streams** | 10 | 10 = all loops converted to streams/collectors; 0 = all imperative loops |
| **Code quality: Exception handling** | 10 | 10 = domain exceptions, logging, DLQ; 5 = caught and logged; 0 = empty catch or swallowed |
| **Modern data carriers** | 10 | 10 = Records with compact constructor validation; 5 = immutable class; 0 = mutable DTO |
| **Concurrency model** | 10 | 10 = virtual threads; 7 = reactive/async; 3 = fixed thread pool; 0 = unmanaged threads |
| **Modern Java features** | 10 | `var`, method references, pattern matching, text blocks, sealed classes — 2pts each |
| **Financial domain rules** | 5 | `ZonedDateTime` over `Date`, `BigDecimal` with `MathContext`, idempotent handlers |
**Total: 100 points**
### How to Report
```
### Modernization Score
| Dimension | Before | After |
|----------------------------|--------|-------|
| NPE prevention | 0/15 | 15/15 |
| Monetary precision | 0/15 | 12/15 |
| Thread safety | 0/15 | 15/15 |
| Streams / collections | 0/10 | 10/10 |
| Exception handling | 0/10 | 6/10 |
| Modern data carriers | 0/10 | 10/10 |
| Concurrency model | 3/10 | 10/10 |
| Modern Java features | 2/10 | 7/10 |
| Financial domain rules | 0/5 | 4/5 |
| **TOTAL** | **5/100** | **89/100** |
**Improvement: +84 points**
```
---
## Financial Domain Rules
**Always Apply:**
- `BigDecimal` for monetary amounts — never `double` or `float`
- `ZonedDateTime` for timestamps — never `Date`
- Validation in record compact constructors
- Immutable domain objects (records, final classes)
- Atomic updates for position/balance changes
- Audit trail through event sourcing
**High-Risk Conversions — Test Thoroughly:**
- Settlement calculations (rounding, precision, `MathContext`)
- Position aggregations (concurrent updates)
- Trade sequencing (event ordering)
Attribution
Comments
Loading comments…