| name | java-rules |
| description | Java coding rules: style, patterns, security, testing. Triggers: .java, pom.xml, build.gradle, Spring, Spring Boot, JPA, Hibernate, JUnit, Maven, Gradle. |
| effort | medium |
| user-invocable | false |
| allowed-tools | Read |
Java Rules
These rules come from app/rules/java/ in ai-toolkit. They cover
the project's standards for coding style, frameworks, patterns,
security, and testing in Java. Apply them when writing or
reviewing Java code.
Java Coding Style
Naming
- PascalCase: classes, interfaces, enums, records, annotations.
- camelCase: methods, variables, parameters.
- UPPER_SNAKE: constants (
static final).
- Package names: lowercase, dot-separated, reverse domain (
com.company.project).
- No Hungarian notation. No
I prefix on interfaces.
Modern Java (17+)
- Use
record for immutable data carriers. No need for Lombok in most cases.
- Use
sealed classes/interfaces for restricted hierarchies.
- Use pattern matching:
if (obj instanceof String s) instead of cast.
- Use
switch expressions with arrow syntax and exhaustiveness.
- Use text blocks (
""") for multiline strings (SQL, JSON, HTML).
Types
- Use
var for local variables when the type is obvious from the right-hand side.
- Use
Optional<T> for return types that may be absent. Never for fields or params.
- Prefer
List.of(), Map.of(), Set.of() for immutable collections.
- Use
Stream for collection transformations. Avoid streams for simple iterations.
Classes
- Prefer composition over inheritance. Use interfaces for abstraction.
- Keep classes focused: single responsibility.
- Use
final on classes not designed for extension.
- Use
private constructors + static factory methods for controlled instantiation.
- Records over POJOs for value types. Lombok only if records are insufficient.
Methods
- Max 20-30 lines per method. Extract when longer.
- Use
@Override on every overridden method.
- Return empty collections over
null. Use Collections.emptyList() or List.of().
- Avoid checked exceptions for programming errors. Use runtime exceptions.
Formatting
- Use project formatter (Google Java Format or IDE-configured).
- Use
@SuppressWarnings sparingly and with specific warning names.
- Use
final for parameters and local variables where practical.
Nullability
- Annotate with
@Nullable / @NonNull from JSpecify or JetBrains.
- Use
Objects.requireNonNull() at public API boundaries.
- Never return
null from collections or arrays. Return empty.
- Use
Optional for genuinely optional return values.
Documentation
- Javadoc on all public classes and methods.
- Use
@param, @return, @throws tags for public API methods.
- Skip Javadoc for obvious getters,
toString(), and equals().
Java Frameworks
Spring Boot
- Use Spring Boot 3+ with Java 17+ minimum.
- Use
@RestController for REST APIs. Return ResponseEntity for status control.
- Use
@Valid + Jakarta Bean Validation for request validation.
- Use profiles (
@Profile) for environment-specific configuration.
- Use
application.yml over application.properties for readability.
- Externalize config: env vars > config files > hardcoded defaults.
Spring Data JPA
- Use repository interfaces extending
JpaRepository.
- Use
@Query with JPQL for custom queries. Use native queries only when needed.
- Use
@EntityGraph to prevent N+1 queries in associations.
- Use
Specification for dynamic query building.
- Always use
@Transactional at the service layer, not repository.
Spring Security
- Use
SecurityFilterChain bean configuration (not WebSecurityConfigurerAdapter).
- Use
@PreAuthorize / @Secured for method-level authorization.
- Use BCrypt for password encoding:
new BCryptPasswordEncoder().
- Configure CORS, CSRF, and session management explicitly.
- Use OAuth2 Resource Server for JWT validation in APIs.
Hibernate / JPA
- Use
FetchType.LAZY by default on all associations.
- Use
@BatchSize or @Fetch(FetchMode.SUBSELECT) to avoid N+1.
- Use
@Version for optimistic locking on entities.
- Use DTOs (records) for read queries. Do not expose entities in APIs.
- Use Flyway or Liquibase for schema migrations.
Quarkus / Micronaut
- Use for microservices and serverless where startup time matters.
- Use compile-time DI (Micronaut) or build-time optimization (Quarkus).
- Use reactive patterns with Mutiny (Quarkus) or Reactor (Micronaut).
- Use native image builds with GraalVM for production deployments.
Build Tools
- Use Gradle (Kotlin DSL) for new projects. Maven for enterprise legacy.
- Use dependency management to unify versions across modules.
- Use Bill of Materials (BOM) imports for consistent Spring versions.
- Use Spotless or Checkstyle for enforced code formatting.
Logging
- Use SLF4J facade with Logback or Log4j2 backend.
- Use structured logging with MDC for correlation IDs.
- Use parameterized logging:
log.info("User {} created", userId).
- Never log sensitive data (passwords, tokens, PII).
Java Patterns
Error Handling
- Use unchecked exceptions for programming errors (
IllegalArgumentException).
- Use checked exceptions only for recoverable conditions the caller must handle.
- Create domain exception hierarchy:
AppException -> NotFoundException, etc.
- Never catch
Exception or Throwable broadly. Catch specific types.
- Use
try-with-resources for all AutoCloseable resources.
Immutability
- Use
record for immutable value objects (Java 16+).
- Use
List.copyOf(), Map.copyOf() to create unmodifiable copies.
- Make fields
private final. No setters unless mutation is required.
- Return defensive copies of mutable collections from getters.
- Use builder pattern for constructing immutable objects with many fields.
Optional
- Use
Optional<T> as return type for methods that may not return a value.
- Chain:
optional.map(...).orElseThrow(...). Avoid isPresent() + get().
- Never use
Optional for fields, method parameters, or collection elements.
- Use
Optional.empty() over null. Use Optional.ofNullable() at boundaries.
Streams
- Use streams for transformations:
filter, map, collect.
- Avoid side effects in stream operations. Keep them pure.
- Use
Collectors.toUnmodifiableList() for immutable results.
- Prefer
for loop for simple iterations that do not transform data.
- Use
Stream.of() or IntStream.range() for generating sequences.
Dependency Injection
- Use constructor injection exclusively. No field or setter injection.
- Accept interfaces in constructors, not implementations.
- Use
@Component, @Service, @Repository for Spring-managed beans.
- Keep the number of constructor dependencies under 5. Split if more.
Concurrency
- Use
ExecutorService and CompletableFuture for async operations.
- Use
virtual threads (Java 21+) for I/O-bound concurrent work.
- Use
ConcurrentHashMap, AtomicInteger for thread-safe operations.
- Avoid
synchronized blocks when possible -- use higher-level concurrency.
- Use
ReentrantReadWriteLock for read-heavy shared state.
Design Patterns
- Use Strategy pattern (via interfaces) over switch/if-else chains.
- Use Factory methods for flexible object creation.
- Use Decorator pattern for composable behavior augmentation.
- Avoid Singleton pattern -- use DI container for lifecycle management.
Anti-Patterns
- Returning
null from methods -- use Optional or empty collections.
- Mutable DTOs with getters/setters -- use records.
- God classes with 20+ dependencies -- split by responsibility.
- String typing for domain values -- use types, enums, or value objects.
Java Security
Input Validation
- Validate all input with Jakarta Bean Validation (
@NotNull, @Size, @Email).
- Use
@Valid on controller parameters to trigger validation automatically.
- Create custom validators for domain-specific rules.
- Never trust client-provided IDs. Verify resource ownership server-side.
SQL Injection
- Use JPA/Hibernate parameterized queries. Never concatenate input into JPQL/SQL.
- Use
CriteriaBuilder or Specifications for dynamic queries.
- For native queries, use named parameters:
@Query(value = "... WHERE id = :id", nativeQuery = true).
- Use
PreparedStatement if using JDBC directly. Never Statement with concatenation.
Authentication
- Use Spring Security with BCrypt (
BCryptPasswordEncoder) for password hashing.
- Use JWT with short expiration (15 min) + refresh tokens for APIs.
- Implement account lockout after N failed attempts.
- Use
@AuthenticationPrincipal to access the current user in controllers.
Authorization
- Use
@PreAuthorize("hasRole('ADMIN')") for role-based access control.
- Use method security for fine-grained authorization.
- Check resource ownership in service layer, not just role membership.
- Default deny: require explicit authorization for every endpoint.
XSS and CSRF
- Spring auto-escapes Thymeleaf output. Do not use
th:utext with user data.
- Enable CSRF protection for session-based auth. Disable only for stateless JWT APIs.
- Set
Content-Type headers explicitly on responses.
- Use CSP headers to restrict script sources.
Serialization
- Do not deserialize untrusted data with
ObjectInputStream (RCE risk).
- Use Jackson with
@JsonIgnoreProperties(ignoreUnknown = true).
- Disable default typing in Jackson: never use
enableDefaultTyping().
- Validate deserialized objects with Bean Validation after parsing.
Dependencies
- Run OWASP Dependency-Check in CI:
mvn verify -P owasp-check.
- Update Spring Boot regularly -- security patches are frequent.
- Use
dependencyManagement to control transitive dependency versions.
- Audit
mvn dependency:tree for unexpected transitive dependencies.
Secrets
- Use Spring Cloud Config or Vault for secrets management.
- Use
@Value("${secret}") with env var placeholders, not hardcoded values.
- Never log request headers containing
Authorization or session tokens.
- Use separate config profiles for dev/staging/prod with different secrets.
Logging Security
- Use parameterized logging to prevent log injection.
- Sanitize user input before logging: remove newlines and control characters.
- Never log stack traces to API responses. Return generic error messages.
Java Testing
Framework
- Use JUnit 5 (Jupiter) for all new tests. No JUnit 4.
- Use AssertJ for fluent, readable assertions.
- Use Mockito for mocking dependencies.
- Use Testcontainers for integration tests with databases/services.
File Naming
- Test classes:
FooTest.java in src/test/java/ mirroring source package.
- Integration tests:
FooIT.java or use @Tag("integration").
- Test utilities:
src/test/java/.../support/ or TestUtils.java.
Structure
- Use
@Nested classes to group related tests within a test class.
- Use
@DisplayName for human-readable test descriptions.
- Use
@BeforeEach for setup, @AfterEach for cleanup.
- Use
@ParameterizedTest with @ValueSource, @CsvSource, @MethodSource.
Assertions (AssertJ)
- Use
assertThat(actual).isEqualTo(expected) over JUnit assertions.
- Use
assertThatThrownBy(() -> ...).isInstanceOf(FooException.class).
- Use
assertThat(list).hasSize(3).extracting("name").contains("Ada").
- Chain assertions for readable, self-documenting tests.
Mocking (Mockito)
- Use
@Mock + @ExtendWith(MockitoExtension.class) for injection.
- Use
when().thenReturn() for stubbing. verify() for interaction checking.
- Use
@InjectMocks to auto-inject mocks into the class under test.
- Prefer constructor injection in production code for testability.
- Use
ArgumentCaptor to inspect complex arguments.
Integration Testing
- Use Testcontainers for PostgreSQL, Redis, Kafka, etc.
- Use
@SpringBootTest sparingly -- it starts the full context. Prefer slices.
- Use
@WebMvcTest for controller tests, @DataJpaTest for repository tests.
- Use
@TestConfiguration for test-specific bean overrides.
Test Data
- Use test builders or factory methods for creating test objects.
- Use
@Sql annotation to load test data from SQL files.
- Keep test data minimal. Only set fields relevant to the behavior under test.
- Use random UUIDs for IDs in tests to avoid collision.
Performance
- Run tests in parallel: configure
junit.jupiter.execution.parallel.enabled=true.
- Use
@SpringBootTest only when integration context is needed.
- Mock external dependencies in unit tests for speed.
- Keep the full test suite under 5 minutes.