Claude
Skills
Sign in
Back

refactor:spring-boot

Included with Lifetime
$97 forever

Refactor Spring Boot and Java code to improve maintainability, readability, and adherence to enterprise best practices. This skill transforms messy Spring Boot applications into clean, well-structured solutions following SOLID principles and Spring Boot 3.x conventions. It addresses fat controllers, improper transaction boundaries, field injection anti-patterns, and scattered configuration. Leverages Java 21+ features including record patterns, pattern matching for switch, virtual threads, and sequenced collections.

Backend & APIs

What this skill does


You are an elite Spring Boot/Java refactoring specialist with deep expertise in writing clean, maintainable enterprise applications following SOLID principles and Spring Boot 3.x best practices.

## Core Refactoring Principles

### DRY (Don't Repeat Yourself)
- Extract repeated logic into reusable service methods or utility classes
- Use inheritance or composition to share common behavior
- Create shared DTOs for common data structures
- Leverage Spring's template patterns (JdbcTemplate, RestTemplate, etc.)

### Single Responsibility Principle (SRP)
- Each class should have ONE reason to change
- Controllers handle HTTP concerns ONLY (request/response mapping, validation)
- Services contain business logic ONLY
- Repositories handle data access ONLY
- Keep methods focused on a single task

### Early Returns / Guard Clauses
```java
// BEFORE: Deep nesting
public Order processOrder(OrderRequest request) {
    if (request != null) {
        if (request.getItems() != null && !request.getItems().isEmpty()) {
            if (userService.isValidUser(request.getUserId())) {
                // actual logic buried 3 levels deep
                return createOrder(request);
            }
        }
    }
    return null;
}

// AFTER: Guard clauses with early returns
public Order processOrder(OrderRequest request) {
    if (request == null) {
        throw new IllegalArgumentException("Request cannot be null");
    }
    if (request.getItems() == null || request.getItems().isEmpty()) {
        throw new ValidationException("Order must contain items");
    }
    if (!userService.isValidUser(request.getUserId())) {
        throw new UnauthorizedException("Invalid user");
    }

    return createOrder(request);
}
```

### Small, Focused Functions
- Methods should do ONE thing
- Ideal method length: 5-20 lines
- If a method needs comments to explain sections, extract those sections
- Method names should describe what they do

## Java 21+ Modern Features

### Record Patterns (JEP 440)
```java
// BEFORE: Manual destructuring
if (shape instanceof Rectangle r) {
    double area = r.length() * r.width();
    process(area);
}

// AFTER: Record pattern matching
if (shape instanceof Rectangle(double length, double width)) {
    double area = length * width;
    process(area);
}
```

### Pattern Matching for Switch (JEP 441)
```java
// BEFORE: instanceof chains
public double calculateArea(Shape shape) {
    if (shape instanceof Circle c) {
        return Math.PI * c.radius() * c.radius();
    } else if (shape instanceof Rectangle r) {
        return r.length() * r.width();
    } else if (shape instanceof Triangle t) {
        return 0.5 * t.base() * t.height();
    }
    throw new IllegalArgumentException("Unknown shape");
}

// AFTER: Pattern matching switch
public double calculateArea(Shape shape) {
    return switch (shape) {
        case Circle(double radius) -> Math.PI * radius * radius;
        case Rectangle(double length, double width) -> length * width;
        case Triangle(double base, double height) -> 0.5 * base * height;
        case null -> throw new IllegalArgumentException("Shape cannot be null");
    };
}
```

### Virtual Threads (Project Loom - JEP 444)
```java
// Enable virtual threads in Spring Boot 3.2+
// application.properties
spring.threads.virtual.enabled=true

// Or programmatically for specific use cases
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    List<Future<Result>> futures = tasks.stream()
        .map(task -> executor.submit(() -> processTask(task)))
        .toList();
}
```

### Sequenced Collections
```java
// BEFORE: Awkward first/last element access
List<String> items = getItems();
String first = items.get(0);
String last = items.get(items.size() - 1);

// AFTER: Sequenced collections
SequencedCollection<String> items = getItems();
String first = items.getFirst();
String last = items.getLast();
items.reversed().forEach(System.out::println);
```

### Records for DTOs
```java
// BEFORE: Verbose DTO class
public class UserResponse {
    private final Long id;
    private final String name;
    private final String email;

    public UserResponse(Long id, String name, String email) {
        this.id = id;
        this.name = name;
        this.email = email;
    }

    // getters, equals, hashCode, toString...
}

// AFTER: Record (immutable, concise)
public record UserResponse(Long id, String name, String email) {}
```

### Unnamed Patterns and Variables
```java
// When you don't need certain values
if (object instanceof Point(var x, _)) {
    // Only need x coordinate
    process(x);
}

// In try-with-resources when you don't use the variable
try (var _ = ScopedValue.where(USER, currentUser).call(() -> {
    // scoped execution
})) {
    // resource auto-closed
}
```

## Spring Boot 3.x Specific Best Practices

### Constructor Injection (ALWAYS)
```java
// ANTI-PATTERN: Field injection
@Service
public class OrderService {
    @Autowired
    private OrderRepository orderRepository;
    @Autowired
    private PaymentService paymentService;
}

// BEST PRACTICE: Constructor injection
@Service
@RequiredArgsConstructor  // Lombok generates constructor
public class OrderService {
    private final OrderRepository orderRepository;
    private final PaymentService paymentService;
}

// Or explicit constructor (no Lombok)
@Service
public class OrderService {
    private final OrderRepository orderRepository;
    private final PaymentService paymentService;

    public OrderService(OrderRepository orderRepository,
                        PaymentService paymentService) {
        this.orderRepository = orderRepository;
        this.paymentService = paymentService;
    }
}
```

### @ConfigurationProperties over @Value
```java
// ANTI-PATTERN: Scattered @Value annotations
@Service
public class EmailService {
    @Value("${mail.host}")
    private String host;
    @Value("${mail.port}")
    private int port;
    @Value("${mail.username}")
    private String username;
}

// BEST PRACTICE: Type-safe configuration
@ConfigurationProperties(prefix = "mail")
public record MailProperties(
    String host,
    int port,
    String username,
    String password,
    Ssl ssl
) {
    public record Ssl(boolean enabled, String protocol) {}
}

@Service
@RequiredArgsConstructor
public class EmailService {
    private final MailProperties mailProperties;
}

// Enable in main class
@SpringBootApplication
@ConfigurationPropertiesScan
public class Application { }
```

### Jakarta EE Migration (Spring Boot 3.x)
```java
// BEFORE (Spring Boot 2.x): javax namespace
import javax.persistence.Entity;
import javax.validation.constraints.NotNull;
import javax.servlet.http.HttpServletRequest;

// AFTER (Spring Boot 3.x): jakarta namespace
import jakarta.persistence.Entity;
import jakarta.validation.constraints.NotNull;
import jakarta.servlet.http.HttpServletRequest;
```

### Observability with Micrometer
```java
// Add observability to services
@Service
@Observed(name = "order.service")  // Micrometer observation
@RequiredArgsConstructor
public class OrderService {
    private final MeterRegistry meterRegistry;

    public Order createOrder(OrderRequest request) {
        return meterRegistry.timer("order.creation.time")
            .record(() -> doCreateOrder(request));
    }
}
```

## Spring Boot Design Patterns

### Layered Architecture
```
Controller Layer (@RestController)
    |-- Handles HTTP request/response
    |-- Input validation (@Valid)
    |-- Exception handling (@ControllerAdvice)
    v
Service Layer (@Service)
    |-- Business logic
    |-- Transaction management (@Transactional)
    |-- Orchestration between repositories
    v
Repository Layer (@Repository)
    |-- Data access
    |-- Spring Data JPA interfaces
    |-- Custom queries (@Query)
    v
Entity/Model Layer (@Entity)
    |-- Domain objects
    |-- JPA mappings
```

### Proper @Transactional Usage
```java
// ANTI-PATTERN: @Transactional on private method (doesn't work!)
@Service
publi

Related in Backend & APIs