agentleFS
glodyfimpaglodyfimpa/claude-skills/java-spring-clean-code/SKILL.md

java-spring-clean-code

Skill0 starsChanged 31 days agoMITView on GitHub

Standard di codice pulito per Java 8-21 LTS e Spring Boot 3. Applica principi Clean Code (KISS, DRY, YAGNI, SOLID), convenzioni di naming, gestione eccezioni, pattern di design e best practice Spring. Usa questo skill quando: (1) scrivi o revisioni codice Java/Spring, (2) fai refactoring per ridurre complessità, (3) crei nuove classi/metodi/test, (4) verifichi conformità SonarQube, (5) implementi architettura a layer Spring Boot. Trigger: "clean code", "refactoring", "code review", "best practice Java", "Spring Boot pattern", "ridurre complessità", "SonarQube".

The file

We split this skill into the rules it gives an agent

2 rules. About 0 are common, 2 are distinctive, and 0 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈2

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 0

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    No flag booleani che modificano comportamento

    line 103
  2. Distinctive:

    Import: no wildcard, ordinati

    line 398

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Java/Spring Clean Code Standards

Standard per scrivere codice Java pulito, moderno e manutenibile con Spring Boot 3. Stack: Java 8-21 LTS + Spring Boot 3.x

Principi Fondamentali

PrincipioSignificatoApplicazione
KISSKeep It SimpleSoluzioni semplici e lineari. Se serve un commento per capire, è troppo complesso
DRYDon't Repeat YourselfOgni concetto una sola rappresentazione. Estrarre logica duplicata
YAGNIYou Aren't Gonna Need ItNon implementare "per il futuro". Complessità solo quando serve
Boy Scout RuleLascia il codice più pulitoOgni commit migliora naming, struttura, rimuove dead code
Fail FastFallire subitoValidare input all'inizio. Errori rilevati presto = debug facile
// KISS: ❌ Over-engineering
public class UserValidatorFactory {
    public static Validator<User> createValidator(ValidationType type) { /*...*/ }
}

// KISS: ✅ Semplice e diretto
public boolean isValidUser(User user) {
    return user.getEmail() != null && user.getEmail().contains("@")
        && user.getName() != null && !user.getName().isBlank();
}

1. Naming & Convenzioni

Principio: I nomi rivelano l'intento (Intention-Revealing Names).

ElementoConvenzioneEsempio
Classi/InterfaccePascalCaseCustomerService, PaymentRepository
MetodicamelCase, verbocalculateTotal(), sendEmail()
VariabilicamelCaseusername, currentOrder
CostantiMAIUSCOLO_SNAKEMAX_RETRY, DEFAULT_TIMEOUT
Packagelowercasecom.company.project.module
Spring beanssuffisso ruolo*Controller, *Service, *Repository

Evitare: nomi generici (util, manager, data), prefissi di tipo (strName), abbreviazioni (usr, cfg).

// ❌ Naming ambiguo
double x = order.getAmount();
double y = calculate(x);

// ✅ Naming chiaro
double orderAmount = order.getAmount();
double discount = calculateDiscount(orderAmount);

2. Dichiarazione & Immutabilità

Principio: Minimizzare scope variabili, dichiarare vicino all'uso.

  • final per variabili immutabili
  • var (Java 10+) solo se tipo evidente dal contesto
  • Costanti nominate invece di magic numbers
  • Non riusare variabili per scopi diversi
// ❌ Magic numbers
double b = a * 0.15;
double c = 5.99;

// ✅ Costanti nominate
private static final double DISCOUNT_RATE = 0.15;
private static final double SHIPPING_COST = 5.99;

final double basePrice = product.getPrice();
final double discount = basePrice * DISCOUNT_RATE;
final double finalPrice = basePrice - discount + SHIPPING_COST;

3. Struttura Classi & Metodi

Principio: Ogni funzione fa "una sola cosa" (SRP), <20 righe, max 3 parametri.

Classi:

  • Coesione: ogni classe = un concetto specifico. No God class
  • Campi: limitare variabili di istanza
  • Incapsulamento: private + getter/setter, evitare obj.getA().getB().getC()
  • Composizione > Ereditarietà

Metodi:

  • Brevi e focalizzati (<20 righe)
  • Nomi descrittivi: sendConfirmationEmail() non processNotification()
  • Max 3-4 parametri, altrimenti DTO
  • No flag booleani che modificano comportamento
  • Guard clauses fail-fast all'inizio
// ✅ Guard clause
public Customer save(Customer customer) {
    if (customer == null) throw new IllegalArgumentException("Customer is null");
    return repository.save(customer);
}

4. Gestione Eccezioni

Principio: Non usare eccezioni per flow normale. Eccezioni invece di codici errore.

// ❌ Ignorare eccezioni
catch(Exception e) { /* ignoring */ }

// ✅ Log e rethrow con contesto
catch(Exception e) {
    log.error("Operation failed for user {}: {}", userId, e.getMessage());
    throw new BusinessException("Unable to complete operation", e);
}

Optional vs Eccezioni vs Null:

SituazioneApproccio
Valore potrebbe mancare (normale)Optional<T>
Errore logico/businessEccezione custom
MaiRitornare null
// ✅ Optional per assenza normale
public Optional<Customer> findByEmail(String email) {
    return Optional.ofNullable(repository.findByEmail(email));
}

// ✅ Eccezione per violazione regola business
public void withdraw(BigDecimal amount) {
    if (balance.compareTo(amount) < 0) {
        throw new InsufficientBalanceException(balance, amount);
    }
}

Spring Transactions: rollback automatico su RuntimeException. Non catturare nel service se vuoi rollback.


5. Commenti & Documentazione

Principio: Codice ben scritto documenta sé stesso. Commenti spiegano il perché, non il cosa.

Commentare:

// Use algorithm X for performance on datasets > 10k elements
// Workaround for bug in library v2.3 - remove after upgrade
// TODO: implement retry logic - JIRA-123

Non commentare:

// increment i  ← RIDONDANTE
i++;

Trasformare commenti in codice:

// ❌ Commento necessario
// check if user can access the resource
if (user.getRole().equals("ADMIN") || /*...*/)

// ✅ Codice auto-documentante
if (canAccessResource(user, resource))

6-8. Java Features (8+, 17+, 21+)

Java 8+

// Lambda & Stream
List<String> emails = users.stream()
    .filter(this::isActiveUser)
    .map(this::normalizeEmail)
    .collect(Collectors.toList());

// Optional
String name = customer.map(Customer::getName).orElse("Anonymous");

Java 17+ (LTS)

// Records - ideali per DTO
public record CustomerDTO(String name, String email, LocalDate birthDate) {}

// Sealed Classes
public sealed interface Payment permits CreditCard, BankTransfer, Cash {}

// Pattern Matching instanceof
if (obj instanceof String s) {
    System.out.println(s.length());
}

// Text Blocks
String query = """
    SELECT u.id, u.name
    FROM users u
    WHERE u.active = true
    """;

// Switch Expressions
String type = switch (customer.getLevel()) {
    case 1, 2 -> "Base";
    case 3, 4 -> "Premium";
    default -> "Unknown";
};

Java 21+ (LTS)

// Virtual Threads - per I/O-bound tasks
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    executor.submit(() -> handleRequest(request));
}

// Sequenced Collections
list.addFirst("first");
list.getLast();
list.reversed();

// Record Patterns
if (obj instanceof Point(int x, int y)) {
    System.out.println(x + y);
}

9. Spring Boot 3 Essentials

Dependency Injection via Costruttore (Best Practice)

@RestController
public class CustomerController {
    private final CustomerService customerService;

    // Constructor injection - @Autowired optional
    public CustomerController(CustomerService customerService) {
        this.customerService = customerService;
    }
}

Perché: dipendenze esplicite, immutabili (final), facile da testare, fail-fast.

Evitare field injection: @Autowired private Service service;

Architettura a Layer

Controller → Service → Repository
    ↓           ↓           ↓
  HTTP      Business     Data Access

Regola: non saltare layer. Controller MAI chiama Repository direttamente.

Controller

@RestController
@RequestMapping("/api/v1/users")
public class UserController {
    private final UserService userService;

    @PostMapping
    public ResponseEntity<UserDTO> create(@RequestBody @Valid CreateUserRequest request) {
        User user = userService.createUser(request);
        return ResponseEntity.status(HttpStatus.CREATED).body(UserDTO.fromEntity(user));
    }
}
  • Thin controllers: minima logica
  • DTO per input/output, non entità JPA
  • @Valid per validazione

Service

@Service
public class UserService {
    @Transactional
    public User createUser(CreateUserRequest request) {
        if (userRepository.existsByEmail(request.email())) {
            throw new UserAlreadyExistsException(request.email());
        }
        return userRepository.save(User.builder()/*...*/.build());
    }

    @Transactional(readOnly = true)
    public Optional<User> findById(Long id) {
        return userRepository.findById(id);
    }
}
  • @Transactional su metodi che modificano
  • @Transactional(readOnly = true) per letture

Global Exception Handler

@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(EntityNotFoundException.class)
    public ResponseEntity<ErrorResponse> handleNotFound(EntityNotFoundException e) {
        return ResponseEntity.status(HttpStatus.NOT_FOUND)
            .body(new ErrorResponse("NOT_FOUND", e.getMessage()));
    }
}

10. SOLID & Design Patterns

PrincipioDescrizioneEsempio
SRPUna classe, una responsabilitàOrderService business, OrderRepository persistenza
OCPAperto estensione, chiuso modificaStrategy pattern per sconti
LSPSottoclassi sostituibiliSquare non estende Rectangle
ISPInterfacce specificheOnlinePayment e OfflinePayment separate
DIPDipendere da astrazioniIniettare interface, non implementazione

Principi Avanzati

CQS (Command Query Separation): metodo = command (void, modifica) OPPURE query (ritorna, no modifica).

Tell Don't Ask: chiedere all'oggetto di fare l'operazione, non chiedere dati per operare.

// ❌ Ask then Do
BigDecimal discount = order.getCustomer().getDiscountRate();
// ✅ Tell
order.applyCustomerDiscount();

Step Down Rule: ordinare metodi dall'alto (pubblici, alto livello) verso il basso (privati, dettagli).

One Level of Abstraction: non mescolare dettagli implementativi con logica alto livello.


11. Smells & Metriche SonarQube

MetricaSogliaCosa Misura
Cognitive Complexity≤ 15/metodoDifficoltà comprensione flusso
Cyclomatic Complexity≤ 10/metodoPercorsi indipendenti
Nesting Depth≤ 3 livelliProfondità annidamento
Method Lines≤ 30 righeLunghezza metodo
Parameters≤ 7Numero parametri
Class Lines≤ 500 righeDimensione classe
Duplicated Lines< 3%Codice duplicato

Pattern per Ridurre Complessità

Early Return:

// ❌ Nesting +3
if (order != null) { if (order.isValid()) { if (order.hasItems()) { /*...*/ }}}

// ✅ Nesting 0
if (order == null) return;
if (!order.isValid()) return;
if (!order.hasItems()) return;
// main logic

Map invece di Switch:

// ❌ Cyclomatic = 5
switch (type) { case "GOLD": return 0.3; /*...*/ }

// ✅ Cyclomatic = 1
private static final Map<String, Double> DISCOUNTS = Map.of("GOLD", 0.3, /*...*/);
return DISCOUNTS.getOrDefault(type, 0.0);

12-13. Formattazione & Package

  • Indentazione: 4 spazi
  • Righe: ≤120 caratteri
  • Graffe: stile K&R
  • Import: no wildcard, ordinati

Package-by-Feature

com.company.project/
├── order/
│   ├── OrderController.java
│   ├── OrderService.java
│   └── Order.java
├── customer/
└── common/

Law of Demeter

// ❌ Violation
order.getCustomer().getAddress().getCity();
// ✅ Correct
order.getShippingCity();

14. Testing

Naming: shouldReturnCustomerWhenIdExists()

Struttura AAA:

@Test
void shouldCalculateTotalWithDiscount() {
    // Arrange
    var order = new Order(BigDecimal.valueOf(100));
    // Act
    BigDecimal total = order.calculateTotal(discount);
    // Assert
    assertThat(total).isEqualByComparingTo(BigDecimal.valueOf(90));
}

Spring Boot Testing:

  • @ExtendWith(MockitoExtension.class) - unit test con mock
  • @SpringBootTest - integration test, full context
  • @WebMvcTest - slice test web layer
  • @DataJpaTest - slice test repository

Coverage: minimo 80%, ideale >90% su business logic.


15. Lombok & JPA Best Practice

@Data su entità JPA = PROBLEMI:

  1. hashCode() cambia dopo save (ID auto-generato)
  2. toString() trigga lazy loading
  3. Relazioni bidirezionali → StackOverflowError

Pattern corretto:

@Entity
@Getter @Setter
@NoArgsConstructor @AllArgsConstructor @Builder
@ToString(onlyExplicitlyIncluded = true)
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class Customer {
    @Id @GeneratedValue
    @EqualsAndHashCode.Include @ToString.Include
    private Long id;

    @ToString.Include
    private String name;

    @OneToMany(mappedBy = "customer", fetch = FetchType.LAZY)
    private List<Order> orders; // Escluso da toString/equals!
}
AnnotazioneSu Entity JPANote
@Getter/@Setter✅Sicuro
@NoArgsConstructor✅Richiesto da JPA
@Builder✅Richiede @NoArgsConstructor
@Data❌EVITARE
@ToString/@EqualsAndHashCode⚠️Solo con onlyExplicitlyIncluded = true

Lombok OK per DTO:

@Data @Builder
public class CustomerDTO { /*...*/ }

// Meglio: Record (Java 17+)
public record CustomerDTO(Long id, String name, String email) {}

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.