java-standards
Skill1 starsChanged 5 months agoMITView on GitHub
Best-practice coding standards for Java code (manifests: pom.xml, build.gradle). Agnostic core plus a Spring Boot overlay. Covers types/records, null safety, error handling, concurrency, security, logging, tooling, testing, plus Spring constructor injection, layering, validation, exception handling, JPA, security. Used by the review agents on Java/Spring projects. Do NOT use for other languages.
In this repository19 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- custom-standardsSkill
- agent-code-to-confirmSkill
- agent-to-confirmSkill
- claude-guideline-creationSkill
- skill-creatorSkill
- youtube-to-guidelineSkill
- youtube-transcribeSkill
- fastapi-standardsSkill
- general-standardsSkill
- go-standardsSkill
- nextjs-standardsSkill
- nodejs-standardsSkill
- python-standardsSkill
- react-standardsSkill
- reviewing-codebasesSkill
- rust-standardsSkill
- skill-toggleSkill
- typescript-standardsSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
25 rules. About 0 are common, 8 are distinctive, and 17 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈8
Distinctive · estimate
This author's own choices. Worth a look.
17
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 36
MUST cite the rule ID: Never renumber.
- Very specific: line 50
Target: all Java source. The core JAVA-* rules apply to any Java code.
- Very specific: line 54
Modern Java: Use current language features (records, sealed types, switch expressions, var, virtual threads) on a supported LTS.
- Very specific: line 64
JAVA-BASE-2 (MUST): Use a declarative build tool — Maven or Gradle — with a committed lockfile/wrapper (mvnw/gradlew) so builds are …
- Very specific: line 68
JAVA-BASE-4 (SHOULD): Keep the happy path shallow: handle errors/edge cases early with guard clauses and early return/throw, rather than …
- Very specific: line 75
JAVA-TYPE-1 (SHOULD): Prefer immutability — final fields, no setters where avoidable.
- Very specific: line 81
JAVA-TYPE-4 (SHOULD): Use var for local variables only when the initializer makes the type obvious.
- Very specific: line 83
JAVA-TYPE-5 (SHOULD NOT): Add an equals/hashCode by hand where a record would do.
- Very specific: line 90
JAVA-NULL-1 (SHOULD): Don't return null for "absent" — return Optional<T> from methods whose result may be missing.
- Very specific: line 94
JAVA-NULL-3 (SHOULD NOT): Call Optional.get() without isPresent().
- Distinctive: line 105
JAVA-ERR-3 (SHOULD): Prefer unchecked exceptions for programming/contract errors.
- Very specific: line 128
JAVA-CONC-1 (MUST): Prefer java.util.concurrent (executors, CompletableFuture, concurrent collections) over raw Thread/synchronized …
- Distinctive: line 133
JAVA-CONC-3 (SHOULD): Use virtual threads (Java 21+) for high-concurrency blocking I/O rather than large platform-thread pools.
- Very specific: line 135
JAVA-CONC-4 (MUST): Always shut executors down (shutdown/try-with-resources on Java 21+ ExecutorService) — no leaked thread pools.
- Very specific: line 157
JAVA-LOG-2 (MUST): Use parameterized logging (log.info("user {} ...", id)), include a level, and MUST NOT log secrets, tokens, or PII.
- Very specific: line 168
JAVA-LINT-2 (SHOULD): Treat warnings as errors where practical.
- Distinctive: line 175
JAVA-TEST-1 (MUST): Test with JUnit 5 (Jupiter) + AssertJ assertions and Mockito for collaborators.
- Distinctive: line 180
JAVA-TEST-3 (SHOULD): Follow the test pyramid — many fast unit tests, fewer integration, few end-to-end.
- Distinctive: line 184
Don't assert on incidental details (log text, private state).
- Distinctive: line 190
JAVA-DOC-1 (SHOULD): Write Javadoc on public API (classes and non-trivial public methods) — what it does, params, return, thrown exceptions …
- Very specific: line 200
JAVA-SPRING-1 (MUST): Use constructor injection (a single constructor, final fields) for dependencies.
- Very specific: line 203
JAVA-SPRING-2 (MUST): Keep the layering clean: @RestController thin (HTTP mapping + validation only), @Service owns business logic, …
- Distinctive: line 207
MUST NOT serialize JPA entities directly out of controllers (leaks the schema and lazy-loading pitfalls).
- Very specific: line 232
JAVA-SPRING-10 (SHOULD): Test with Spring's test slices — @WebMvcTest (controllers, via MockMvc/MockMvcTester), @DataJpaTest …
- Very specific: line 235
JAVA-SPRING-11 (SHOULD): Default web stack is Spring MVC (servlet.
≈ “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.
Files
Java Coding Standard (v1.0)
Status: Locked v1.0. Same structure as the locked
nodejs.md: an agnostic core (JAVA-*, the language + general practices) plus a framework overlay (JAVA-SPRING-*) for the default backend stack — Spring Boot — exactly asnodejs.mdlayers a NestJS overlay on its core. The overlay mechanically enforces the core and stays cross-platform consistent withnodejs.md,fastapi.md, andgo.mdon auth, API versioning, and the data layer.On adoption this becomes
.claude/skills/java-standards/SKILL.md+references/*.md.
Toolchain verified current as of 2026-06-13 (via Context7 + Maven Central/Adoptium): Java LTS 21 & 25 (25 is current LTS), Spring Boot 4.1.x. Rules are written version-agnostically; these are reference points, not pins.
How to read this document
Constraint language (RFC 2119)
- MUST / MUST NOT — hard requirement; a violation is a blocking issue.
- SHOULD / SHOULD NOT — strong recommendation; deviate only with a written
// Justification:. - MAY — optional.
Rule IDs
Stable JAVA-AREA-NUMBER (e.g. JAVA-ERR-3); Spring overlay uses JAVA-SPRING-N. The reviewer
MUST cite the rule ID. Never renumber.
Severity mapping
| This document says… | Reviewer severity | Gate |
MUST/MUST NOT affecting safety / security | Critical | Blocks merge |
MUST/MUST NOT elsewhere | Critical / Warning (by impact) | Blocks if it risks correctness |
SHOULD without justification | Warning | Should fix |
MAY / stylistic | Suggestion | Nice to have |
Scope & Assumptions
- Target: all Java source. The core
JAVA-*rules apply to any Java code; theJAVA-SPRING-*overlay applies to Spring Boot services (the default backend stack). - Backend default is Spring Boot — named, not mandated for non-service code (libraries, CLIs, batch jobs follow the core only).
- Modern Java. Use current language features (records, sealed types, switch expressions,
var, virtual threads) on a supported LTS. - Custom overrides: the project's
custom-standardsskill overrides this document on conflict.
1. Language Baseline & Build
- JAVA-BASE-1 (MUST): Target a supported LTS (currently 21 or 25). Declare it via the
build tool's
release/toolchain so source, target, and bytecode agree. - JAVA-BASE-2 (MUST): Use a declarative build tool — Maven or Gradle — with a committed
lockfile/wrapper (
mvnw/gradlew) so builds are reproducible. Pin plugin and dependency versions. - JAVA-BASE-3 (MUST): Code MUST be auto-formatted by a committed formatter (Spotless with google-java-format or Palantir) enforced in CI — formatting is not reviewed by hand.
- JAVA-BASE-4 (SHOULD): Keep the happy path shallow: handle errors/edge cases early with guard
clauses and early
return/throw, rather than deepif/elsenesting.
2. Types & Object Design
- JAVA-TYPE-1 (SHOULD): Prefer immutability —
finalfields, no setters where avoidable. Userecordfor immutable data carriers (DTOs, value objects, events) instead of hand-written POJOs. - JAVA-TYPE-2 (SHOULD): Model closed hierarchies with
sealedinterfaces/classes + pattern- matchingswitchso the compiler enforces exhaustiveness when a case is added. - JAVA-TYPE-3 (SHOULD): Program to interfaces for collaborators; keep them small and defined by
the consumer's need. Don't expose mutable collections — return unmodifiable/
List.copyOfviews. - JAVA-TYPE-4 (SHOULD): Use
varfor local variables only when the initializer makes the type obvious; keep explicit types on fields, method signatures, and public API. - JAVA-TYPE-5 (SHOULD NOT): Add an
equals/hashCodeby hand where arecordwould do; if you do override them, override both (andtoString) consistently.
3. Null Safety
- JAVA-NULL-1 (SHOULD): Don't return
nullfor "absent" — returnOptional<T>from methods whose result may be missing. MUST NOT useOptionalfor fields or method parameters. - JAVA-NULL-2 (SHOULD): Validate arguments at public boundaries (
Objects.requireNonNull, or Bean Validation in Spring) and document nullability; keep internal code null-free by construction. - JAVA-NULL-3 (SHOULD NOT): Call
Optional.get()withoutisPresent(); prefermap/orElse/orElseThrow.
4. Error Handling
- JAVA-ERR-1 (MUST): Throw specific exceptions; MUST NOT catch
Exception/Throwablebroadly and swallow it. An empty or log-onlycatchthat hides a failure is a blocking issue. - JAVA-ERR-2 (MUST): Manage resources with try-with-resources (any
AutoCloseable) — never rely on manualfinallyclose. Don't leak streams, connections, or clients. - JAVA-ERR-3 (SHOULD): Prefer unchecked exceptions for programming/contract errors; use checked
exceptions only when the caller can realistically recover. When wrapping, preserve the cause
(
throw new XException("...", e)) — never drop the stack trace. - JAVA-ERR-4 (SHOULD): Define a small set of domain exception types the application maps to
consistent responses (see
JAVA-SPRING-6). Don't use exceptions for normal control flow. - JAVA-ERR-5 (MUST NOT): Log-and-rethrow the same exception at every layer — log it once, where it's handled, to avoid duplicate stack traces.
// ✅ JAVA-ERR-2/3: try-with-resources + preserve cause
public String read(Path path) {
try (var reader = Files.newBufferedReader(path)) {
return reader.lines().collect(Collectors.joining("\n"));
} catch (IOException e) {
throw new ConfigLoadException("failed to read " + path, e); // cause preserved
}
}5. Concurrency
- JAVA-CONC-1 (MUST): Prefer
java.util.concurrent(executors,CompletableFuture, concurrent collections) over rawThread/synchronizedhand-rolling. MUST NOT share mutable state without synchronization or confinement. - JAVA-CONC-2 (SHOULD): Favor immutable objects and stateless services for thread-safety; document any class that is not thread-safe.
- JAVA-CONC-3 (SHOULD): Use virtual threads (Java 21+) for high-concurrency blocking I/O rather than large platform-thread pools; keep CPU-bound work on a bounded pool.
- JAVA-CONC-4 (MUST): Always shut executors down (
shutdown/try-with-resources on Java 21+ExecutorService) — no leaked thread pools.
6. Security
- JAVA-SEC-1 (MUST NOT): Build SQL/HQL/JPQL by string concatenation. Use parameterized queries /
bound parameters (
JAVA-SPRING-7). - JAVA-SEC-2 (MUST NOT): Hard-code secrets. Externalize credentials/keys to environment or a secret manager; never commit them.
- JAVA-SEC-3 (MUST): Hash passwords with a memory-hard KDF (bcrypt/argon2 via Spring Security's
PasswordEncoder); useSecureRandom(neverjava.util.Random) for tokens/salts. - JAVA-SEC-4 (MUST): Scan dependencies for known CVEs (OWASP Dependency-Check or equivalent) in CI; keep them patched. MUST NOT deserialize untrusted data with native Java serialization.
7. Logging & Observability
- JAVA-LOG-1 (MUST): Log via the SLF4J API (backed by Logback/Log4j2). MUST NOT use
System.out/System.errorprintStackTrace()for application logging. - JAVA-LOG-2 (MUST): Use parameterized logging (
log.info("user {} ...", id)), include a level, and MUST NOT log secrets, tokens, or PII. (Consistent withNODE-LOG-2.) - JAVA-LOG-3 (SHOULD): Carry a correlation/trace ID (MDC) and expose health/readiness + metrics
(Spring Boot Actuator). (Consistent with
NODE-LOG-3.)
8. Tooling & Static Analysis
- JAVA-LINT-1 (MUST): Static analysis MUST pass in CI — at least Error Prone or SpotBugs, plus Checkstyle (or Spotless's checks) for conventions.
- JAVA-LINT-2 (SHOULD): Treat warnings as errors where practical; suppress a single finding inline
with a justified
@SuppressWarnings("...") // reason, never blanket-disable a check.
9. Testing
- JAVA-TEST-1 (MUST): Test with JUnit 5 (Jupiter) + AssertJ assertions and Mockito for collaborators. New or changed behavior ships with tests; changed logic with no test is insufficient coverage.
- JAVA-TEST-2 (MUST): Tests are deterministic and isolated — no real external network in unit tests (mock at the boundary), no order dependency, no shared mutable state.
- JAVA-TEST-3 (SHOULD): Follow the test pyramid — many fast unit tests, fewer integration, few
end-to-end. Target ≥ 80% coverage on changed code (not whole-repo) via JaCoCo; coverage is a
floor, not the goal. (Consistent with
NODE-TEST-3/PY-TEST-4.) - JAVA-TEST-4 (SHOULD): Name tests by behavior; use parameterized tests for table-style cases. Don't assert on incidental details (log text, private state).
10. Documentation
- JAVA-DOC-1 (SHOULD): Write Javadoc on public API (classes and non-trivial public methods) — what it does, params, return, thrown exceptions — not restatements of the signature.
Spring Boot Overlay (JAVA-SPRING-*)
Applies when the service is a Spring Boot app. These rules mechanically enforce the core and keep the service consistent with
nodejs.md/fastapi.md/go.md.
- JAVA-SPRING-1 (MUST): Use constructor injection (a single constructor,
finalfields) for dependencies. MUST NOT use field injection (@Autowiredon fields) — it hides dependencies and breaks testability/immutability. - JAVA-SPRING-2 (MUST): Keep the layering clean:
@RestControllerthin (HTTP mapping + validation only),@Serviceowns business logic,@Repositoryowns data access. No business logic in controllers; no HTTP types in the service layer. - JAVA-SPRING-3 (MUST): Expose DTOs (prefer
records —JAVA-TYPE-1) at the API boundary; MUST NOT serialize JPA entities directly out of controllers (leaks the schema and lazy-loading pitfalls). For DTO↔entity mapping, MapStruct (compile-time, type-safe) is the recommended default — recommended not mandated; explicit hand-written mapping is also fine. Avoid reflection-based runtime mappers. - JAVA-SPRING-4 (MUST): Validate request bodies/params with Jakarta Bean Validation —
@Valid @RequestBody+ constraints (@NotNull,@Email, …). Treat all request input as untrusted. - JAVA-SPRING-5 (SHOULD): Externalize config via
@ConfigurationProperties(typed,@Validated) and profiles; never hard-code environment values. Secrets come from env/secret manager (JAVA-SEC-2). - JAVA-SPRING-6 (MUST): Handle errors centrally with
@ControllerAdvice+@ExceptionHandler, mapping domain exceptions (JAVA-ERR-4) to consistentResponseEntitypayloads with correct status codes. MUST NOT leak stack traces or internal messages to clients. - JAVA-SPRING-7 (MUST): Access data through Spring Data JPA repositories (or JDBC) with bound
parameters — never string-built queries (
JAVA-SEC-1). Manage schema with versioned migrations via Flyway or Liquibase (both first-class Spring Boot auto-configured peers — Flyway is the common pick for SQL-first projects; neither is mandated over the other); neverddl-auto: updatein production. (Consistent with the repo-layer + migrations default inNODE-NEST-16/FASTAPI-STRUCT-5/GO-API-6.) - JAVA-SPRING-8 (SHOULD): Secure endpoints with Spring Security; default scheme is stateless
JWT bearer (resource-server /
Authorization: Bearer), checking authorization (roles/scopes), not just authentication. Sanctioned alternatives by context: httpOnly session cookies (browser-first) or an external IdP / OAuth2 provider. (Consistent withNODE-NEST-14/FASTAPI-SEC-1/GO-API-4.) - JAVA-SPRING-9 (SHOULD): Version public APIs with URI versioning
/v1on the controller base path. (Consistent withNODE-NEST-15/FASTAPI-DOC-2/GO-API-5.) - JAVA-SPRING-10 (SHOULD): Test with Spring's test slices —
@WebMvcTest(controllers, viaMockMvc/MockMvcTester),@DataJpaTest(repositories),@SpringBootTestonly for true integration — rather than booting the whole context for every test. - JAVA-SPRING-11 (SHOULD): Default web stack is Spring MVC (servlet; pairs well with virtual
threads —
JAVA-CONC-3). WebFlux (reactive, Reactor Netty) is a sanctioned alternative for streaming or high-concurrency non-blocking workloads — don't adopt reactive types (Mono/Flux) without that need, as they add real complexity.
// ✅ JAVA-SPRING-1/2/3/4: constructor injection, thin controller, DTO record, @Valid
@RestController
@RequestMapping("/v1/users") // JAVA-SPRING-9
public class UserController {
private final UserService users; // JAVA-SPRING-1 (final, constructor-injected)
public UserController(UserService users) {
this.users = users;
}
@PostMapping
public ResponseEntity<UserResponse> create(@Valid @RequestBody CreateUserRequest req) {
var user = users.create(req.name(), req.email()); // logic lives in the service
return ResponseEntity.status(HttpStatus.CREATED).body(UserResponse.from(user));
}
}
public record CreateUserRequest(@NotBlank String name, @Email String email) {} // JAVA-SPRING-3/4Resolved Decisions (v1.0)
| # | Question | Decision | Rule(s) |
| 1 | Build tool | Gradle and Maven both sanctioned as equal-tier peers — not a default+fallback. Matches the un-ranked-peers pattern (GO-API-6 goose/golang-migrate; NODE-TEST-4 runner set) | JAVA-BASE-2 |
| 2 | Web stack | Spring MVC default (servlet, virtual-thread-friendly); WebFlux sanctioned for reactive/high-concurrency non-blocking. Matches NODE-NEST-14 "named default + sanctioned-by-context" | JAVA-SPRING-11 |
| 3 | Mapping | MapStruct (compile-time) recommended default for DTO↔entity, recommended not mandated; hand-written mapping fine; avoid reflection-based mappers. Matches GO-API-6 / FASTAPI-STRUCT-5 form | JAVA-SPRING-3 |
| 4 | Migrations | Flyway + Liquibase both sanctioned first-class peers (Flyway common for SQL-first; neither mandated). Matches GO-API-6 two-un-ranked precedent — not the single-Alembic case (Alembic is singular only as THE SQLAlchemy tool) | JAVA-SPRING-7 |
Toolchain reference (verified 2026-06-13): MapStruct 1.6.3, Flyway 11.8.2, Liquibase 4.32.0 — alongside Java LTS 21/25 and Spring Boot 4.1.x.
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
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.

