Write idiomatic, modern Java
Add Java that uses the current language toolset, models data immutably, and matches the
repo's existing idioms — provably correct and conventional, not just compiling.
Steps
- Read the lore first. Call
search_lore (Memory MCP) for the repo's Java
conventions and target version, and respect the build config (pom.xml /
build.gradle), the formatter (Spotless / google-java-format), and any
architecture ADRs. Match the Java version the project already compiles against —
do not assume the newest.
- Find a sibling class and copy its patterns — package layout, naming, error
handling, how DTOs/entities are modelled, and how tests are organised.
- Use modern language features where the version allows. Prefer records for
data carriers over hand-written getters/setters or Lombok
@Data/@Value/@Builder.
Use sealed interfaces + pattern matching and switch expressions to model
closed hierarchies exhaustively; use text blocks for multi-line literals.
- Discipline with
Optional. Represent absence with Optional rather than null;
never call .get() without an isPresent() guard — prefer .map(), .orElseThrow(),
.orElseGet(). Never use Optional for fields or method parameters.
- Favour immutability. Final fields, immutable collections (
List.copyOf),
defensive copies at boundaries. Prefer composition over inheritance; extract an
interface when it improves testability.
- Spring Boot idioms (if applicable). Constructor injection (no field
@Autowired);
keep controllers thin and push logic to services; validate request bodies at the
boundary (@Valid + Bean Validation). Log via SLF4J (LoggerFactory.getLogger) —
never log secrets, tokens, or PII.
- Streams over loops where readability isn't sacrificed; keep methods small and
single-purpose.
- Test with JUnit 5 + Mockito. Use
@ExtendWith(MockitoExtension.class), @Mock/
@InjectMocks, AssertJ-style assertions, and cover happy path, edge cases, and error
conditions. Use Testcontainers for integration tests touching real infrastructure.
- Verify + evidence. Run the build's test goal, record
test_output via the
record-evidence skill, and submit for review.
Build / Test
- Maven:
mvn test (unit), mvn verify (full, incl. coverage), mvn package.
- Gradle:
./gradlew test, ./gradlew jacocoTestReport, ./gradlew build.
- The DoD is verified by the repo's configured test/coverage commands — run them and
record the output; a green run with coverage is the evidence, not a claim that it passes.
- Follow the Google Java Style Guide; run the project's formatter (Spotless /
google-java-format) so the diff is style-clean before review.
Review checklist (a Java reviewer must check)
- Records over Lombok for data carriers; no new
@Data/@Value/@Builder.
Optional used correctly — no unguarded .get(), no Optional fields/params,
no null where Optional fits.
- Sealed hierarchies are exhaustive — switch expressions over a sealed type have no
default that hides a missing case.
- Immutability — fields
final where possible, collections defensively copied at
boundaries; no leaking internal mutable state.
- Spring: constructor injection (not field
@Autowired); controllers thin; inputs
validated; no business logic in controllers.
- Logging: SLF4J, parameterised (
log.info("x={}", x)), and no secrets/PII in
logs or exception messages.
- No swallowed exceptions — caught exceptions are handled or rethrown with context,
never silently dropped.
- Tests use JUnit 5 + Mockito (
@ExtendWith(MockitoExtension.class)) and cover error
paths, not only the happy path.
Rules
- Match the repo's Java version, build tool, and formatter exactly — never widen them to
silence an error.
- Records over Lombok;
Optional not null; sealed + pattern matching for closed sets.
- Constructor injection in Spring; never log secrets or PII.
- No empty catch blocks — handle or rethrow with context.
Capture lore
This skill is one of the places durable, reusable knowledge naturally surfaces:
A Java convention this repo enforces beyond the obvious — a target version constraint, an immutability or layering rule, a build/formatter gotcha, or a Spring wiring pattern. That kind of fact is lore. Capture it via the lore-capture
protocol in your brief (CLAUDE.factory.md, step 11 "Memory contribution"):
call the Memory MCP suggest_lore once at the close of your work — reusable
conventions, gotchas, decisions, and boundaries only, never per-ticket trivia.
1---2name: java-conventions3description: Use when a ticket adds or changes Java code and it must follow the repo's Java conventions — modern Java (records, sealed types, pattern matching, switch expressions), Optional discipline, immutability, Spring Boot constructor injection, and JUnit 5 + Mockito tests. Invoke for "add this in Java", "fix the Java build", "add a Spring endpoint/service", or as the language pack for any Java change.4---56# Write idiomatic, modern Java78Add Java that uses the current language toolset, models data immutably, and matches the9repo's existing idioms — provably correct and conventional, not just compiling.1011## Steps12131. **Read the lore first.** Call `search_lore` (Memory MCP) for the repo's Java14 conventions and target version, and respect the build config (`pom.xml` /15 `build.gradle`), the formatter (Spotless / google-java-format), and any16 architecture ADRs. Match the Java version the project already compiles against —17 do not assume the newest.182. **Find a sibling class** and copy its patterns — package layout, naming, error19 handling, how DTOs/entities are modelled, and how tests are organised.203. **Use modern language features where the version allows.** Prefer **records** for21 data carriers over hand-written getters/setters or Lombok `@Data`/`@Value`/`@Builder`.22 Use **sealed** interfaces + **pattern matching** and **switch expressions** to model23 closed hierarchies exhaustively; use text blocks for multi-line literals.244. **Discipline with `Optional`.** Represent absence with `Optional` rather than `null`;25 never call `.get()` without an `isPresent()` guard — prefer `.map()`, `.orElseThrow()`,26 `.orElseGet()`. Never use `Optional` for fields or method parameters.275. **Favour immutability.** Final fields, immutable collections (`List.copyOf`),28 defensive copies at boundaries. Prefer composition over inheritance; extract an29 interface when it improves testability.306. **Spring Boot idioms (if applicable).** Constructor injection (no field `@Autowired`);31 keep controllers thin and push logic to services; validate request bodies at the32 boundary (`@Valid` + Bean Validation). Log via **SLF4J** (`LoggerFactory.getLogger`) —33 **never log secrets, tokens, or PII**.347. **Streams over loops** where readability isn't sacrificed; keep methods small and35 single-purpose.368. **Test with JUnit 5 + Mockito.** Use `@ExtendWith(MockitoExtension.class)`, `@Mock`/37 `@InjectMocks`, AssertJ-style assertions, and cover happy path, edge cases, and error38 conditions. Use Testcontainers for integration tests touching real infrastructure.399. **Verify + evidence.** Run the build's test goal, record `test_output` via the40 `record-evidence` skill, and submit for review.4142## Build / Test4344- **Maven:** `mvn test` (unit), `mvn verify` (full, incl. coverage), `mvn package`.45- **Gradle:** `./gradlew test`, `./gradlew jacocoTestReport`, `./gradlew build`.46- The DoD is verified by the repo's configured test/coverage commands — run them and47 record the output; a green run with coverage is the evidence, not a claim that it passes.48- Follow the Google Java Style Guide; run the project's formatter (Spotless /49 google-java-format) so the diff is style-clean before review.5051## Review checklist (a Java reviewer must check)5253- **Records over Lombok** for data carriers; no new `@Data`/`@Value`/`@Builder`.54- **`Optional` used correctly** — no unguarded `.get()`, no `Optional` fields/params,55 no `null` where `Optional` fits.56- **Sealed hierarchies are exhaustive** — switch expressions over a sealed type have no57 default that hides a missing case.58- **Immutability** — fields `final` where possible, collections defensively copied at59 boundaries; no leaking internal mutable state.60- **Spring:** constructor injection (not field `@Autowired`); controllers thin; inputs61 validated; no business logic in controllers.62- **Logging:** SLF4J, parameterised (`log.info("x={}", x)`), and **no secrets/PII** in63 logs or exception messages.64- **No swallowed exceptions** — caught exceptions are handled or rethrown with context,65 never silently dropped.66- **Tests** use JUnit 5 + Mockito (`@ExtendWith(MockitoExtension.class)`) and cover error67 paths, not only the happy path.6869## Rules7071- Match the repo's Java version, build tool, and formatter exactly — never widen them to72 silence an error.73- Records over Lombok; `Optional` not `null`; sealed + pattern matching for closed sets.74- Constructor injection in Spring; never log secrets or PII.75- No empty catch blocks — handle or rethrow with context.7677## Capture lore7879This skill is one of the places durable, reusable knowledge naturally surfaces:80**A Java convention this repo enforces beyond the obvious — a target version constraint, an immutability or layering rule, a build/formatter gotcha, or a Spring wiring pattern.** That kind of fact is *lore*. Capture it via the **lore-capture81protocol in your brief** (`CLAUDE.factory.md`, step 11 "Memory contribution"):82call the Memory MCP `suggest_lore` once at the close of your work — reusable83conventions, gotchas, decisions, and boundaries only, never per-ticket trivia.