Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsBlogPro
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Authors
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges
  • Chrome Extension
  • Skill Manager

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

ProTermsPrivacyRefunds
Back to skills

Java Clean General

ASecurity

Enforces general code quality in Java 21+ — DRY, no magic numbers, immutability by default, sealed hierarchies and pattern matching over instanceof chains, encapsulation over getters and setters, and streams used where they clarify. Use when reviewing or refactoring Java for quality, and when the code shows duplication, hardcoded values, long if-else or switch chains on type, mutable shared state, anaemic classes, deep call chains like a.getB().getC().getD(), or clever one-liners. Also trigge...

2 stars
0 votes
0 copies
1 views
Added 9/19/2026
developmentgojavarefactoringapi

Works with

cliapi

Security Analysis

A100/100

Scanned 9/19/2026

$npx -y skills add CasLubbers/code-design-skills --skill java-clean-general --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Java Clean General?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Java Clean General
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/caslubbers-java-clean-general/badge)](https://www.skillsdirectory.com/skills/caslubbers-java-clean-general)

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

Download with Pro
Files
SKILL.md
---
name: java-clean-general
description: Enforces general code quality in Java 21+ — DRY, no magic numbers, immutability by default, sealed hierarchies and pattern matching over instanceof chains, encapsulation over getters and setters, and streams used where they clarify. Use when reviewing or refactoring Java for quality, and when the code shows duplication, hardcoded values, long if-else or switch chains on type, mutable shared state, anaemic classes, deep call chains like a.getB().getC().getD(), or clever one-liners. Also trigger on: a class extending another to reuse one method, an override throwing UnsupportedOperationException, a hierarchy three levels deep, an abstract class standing in for a sealed set of cases, or asks like "should this extend or compose", "composition over inheritance".
---

# General clean code in Java

## DRY

Duplicated logic means every fix must be found in several places. Extract it to a method, a shared class, or a base type — once you see it a third time and the duplicates truly say the same thing.

Duplication that is coincidental is not duplication. Two rules that happen to compute the same number today, for different reasons, should stay apart.

## No magic values

```java
// Bad
if (user.age() >= 18 && order.total() > 100) { applyDiscount(order, 0.15); }

// Good
private static final int LEGAL_ADULT_AGE = 18;
private static final Money FREE_SHIPPING_THRESHOLD = Money.euros(100);
private static final BigDecimal LOYALTY_DISCOUNT = new BigDecimal("0.15");
```

Money is `BigDecimal` or a dedicated type, never `double` — binary floating point cannot represent 0.10, and the rounding error becomes a support ticket.

## Immutable by default

```java
// Good — a record is final, its components final, equals and hashCode supplied
public record Money(BigDecimal amount, Currency currency) {
    public Money {
        Objects.requireNonNull(amount);
        if (amount.scale() > 2) throw new IllegalArgumentException("max 2 decimals");
    }
    public Money plus(Money other) { return new Money(amount.add(other.amount), currency); }
}
```

The compact constructor is where invariants are enforced — an object that cannot be constructed invalid never needs validating again. Mark fields `final` unless they must change, return `List.copyOf(...)` rather than the internal list, and prefer a new instance over a mutating setter.

## Sealed types and pattern matching over type checks

```java
// Bad — a chain that must be edited in five places when a shape is added
if (shape instanceof Circle) { ... }
else if (shape instanceof Square) { ... }

// Good — the compiler enforces exhaustiveness
public sealed interface Shape permits Circle, Square, Rectangle {}

double area = switch (shape) {
    case Circle c    -> Math.PI * c.radius() * c.radius();
    case Square s    -> s.side() * s.side();
    case Rectangle r -> r.width() * r.height();
};
```

Adding a permitted subtype breaks the build at every switch that must handle it — which is what you want. Record patterns destructure in place: `case Circle(double radius) -> Math.PI * radius * radius`.

Behaviour that belongs to the type itself still belongs on the type. Use polymorphism when each case is the type's own business; use a sealed switch when the operation belongs to the caller.

## Tell, don't ask

```java
// Bad — anaemic object, logic outside it
if (account.getBalance().compareTo(amount) >= 0) {
    account.setBalance(account.getBalance().subtract(amount));
}

// Good — the object enforces its own rule
account.withdraw(amount);
```

A class that is only getters and setters is a struct with ceremony. Move the behaviour that operates on its data into it, and stop exposing the internals that behaviour needs.

## Don't chain through objects

```java
// Bad — knows the whole graph; any change breaks it
order.getCustomer().getAddress().getCountry().getCode();

// Good
order.shippingCountryCode();
```

Ask the object you have for what you need, not for its neighbours.

## Streams where they clarify

```java
// Good
var activeEmails = users.stream()
    .filter(User::isActive)
    .map(User::email)
    .toList();
```

A stream that needs a comment to follow should be a loop. Do not use `forEach` for side effects on a collection you are building, do not nest streams three deep, and do not chase cleverness — `toList()` beats `collect(Collectors.toList())`, and a plain `for` beats a contorted pipeline.

## Handle the boundaries

Null checks at the edges (`Objects.requireNonNull` in constructors), validation where data enters, empty collections rather than null, and `Optional` for absent return values. Inside a well-guarded core, stop re-checking.

## Composition over inheritance

Inheritance is permanent coupling to a parent's internals. Use it only where the subtype is
substitutable everywhere the parent is; for reuse, hold a field.

```java
// Bad — extends to borrow send(), inherits the whole SMTP surface as its own API
class EmailNotifier extends SmtpClient {
    void notify(User user, String message) { send(user.email(), message); }
}

// Good — holds what it needs, exposes only notify
final class EmailNotifier {
    private final SmtpClient smtp;

    EmailNotifier(SmtpClient smtp) { this.smtp = smtp; }

    void notify(User user, String message) { smtp.send(user.email(), message); }
}
```

The composed version takes a fake `SmtpClient` in tests and can swap transports. Signals you
extended for the wrong reason: an override that throws `UnsupportedOperationException`, an override
ignoring parameters the base requires, or a three-deep hierarchy with the behaviour in the middle.

Where subclasses were only ever standing in for a fixed set of cases, a sealed interface plus
records says it better — and the compiler checks exhaustiveness. Use an interface to share a
contract, a field to share behaviour, and `abstract class` only for genuine shared state with an
invariant to protect.

## Structure

One public class per file, named after the file. Fields private. Methods ordered so callers appear
above callees, so the file reads top-down (`java-clean-functions` covers the stepdown rule). Keep
classes focused — a class needing "and" to describe it is two classes. `final` on a class you do not
intend to be extended.

Attribution

CasLubbersCasLubbers
View sourceSee grades on GitHubMore from CasLubbers →
SSkills Directory ProSkills Directory

Get any skill into Claude in one click.

Download any skill as a ZIP for Claude.ai, Claude Desktop, or .claude/skills. $9/mo.

See Pro

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 (0)

No comments yet. Be the first to comment!

SSkills Directory ProSkills Directory

Get any skill into Claude in one click.

Download any skill as a ZIP for Claude.ai, Claude Desktop, or .claude/skills. $9/mo.

See Pro

Related Skills

Clean Code

Pragmatic coding standards - concise, direct, no over-engineering, no unnecessary comments

304955 votes

Browser Extension Developer

Use this skill when developing or maintaining browser extension code in the `browser/` directory, including Chrome/Firefox/Edge compatibility, content scripts, background scripts, or i18n updates.

285172 votes

Seo Optimizer

SEO optimization with keyword analysis, readability assessment, technical validation, content quality. Use for search rankings, blog posts, content audits, or encountering keyword density, readability scores, meta tags, schema markup errors.

2222 votes

Google Official Seo Guide

Official Google SEO guide covering search optimization, best practices, Search Console, crawling, indexing, and improving website search visibility based on official Google documentation

1862 votes

Tanstack Start

Build a full-stack TanStack Start app on Cloudflare Workers from scratch — SSR, file-based routing, server functions, D1+Drizzle, better-auth, Tailwind v4+shadcn/ui. Use whenever the user mentions TanStack Start, asks to scaffold a full-stack Cloudflare app with SSR, wants an SSR dashboard, or asks for a React 19 + Cloudflare Workers app with file-based routing and server functions — even if they don't name TanStack Start specifically. No template repo — Claude generates every file fresh per ...

10311 votes
View all in development →