Skip to content
Back to skills

Java Modernization Review

ASecurity

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.

  • 4 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 24, 2026
developmentgojavasqlreactexpressspringtestingdebuggingrefactoringapi

Works with

  • cli
  • api

Security analysis

A100/100

Scanned September 24, 2026

npx -y skills add Santoshrt999/Java-Claude-Skills --skill java-modernization-review --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Java Modernization Review?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Java Modernization Review
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/santoshrt999-java-modernization-review/badge)](https://www.skillsdirectory.com/skills/santoshrt999-java-modernization-review)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
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

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…