java-coding-standards — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited java-coding-standards (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Standards for readable, maintainable Java (21+) code in Spring Boot services.
// PASS: Classes/Records: PascalCase
public class MarketService {}
public record Money(BigDecimal amount, Currency currency) {}
// PASS: Methods/fields: camelCase
private final MarketRepository marketRepository;
public Market findBySlug(String slug) {}
// PASS: Constants: UPPER_SNAKE_CASE
private static final int MAX_PAGE_SIZE = 100;Boolean variables and methods must start with is, has, or can.
Method names must be descriptive verbs: calculateTotalRevenue, findActiveUsers.
Use Lombok to eliminate boilerplate. Required annotations:
@Slf4j: mandatory for all logging; never use LoggerFactory.getLogger(...) manually@RequiredArgsConstructor: constructor injection@Builder: for complex object constructionConstructor injection only; never field injection, never @Autowired on a field.
@Value: for immutable value objects@Getter / @Setter: only when not using @Value or recordsPrefer annotation-based configuration over manual wiring.
// PASS: Favor records and final fields
public record MarketDto(Long id, String name, MarketStatus status) {}
public class Market {
private final Long id;
private final String name;
// getters only, no setters
}// PASS: Return Optional from find* methods
Optional<Market> market = marketRepository.findBySlug(slug);
// PASS: Map/flatMap instead of get()
return market
.map(MarketResponse::from)
.orElseThrow(() -> new EntityNotFoundException("Market not found"));Optional as return type ONLY, not for fields or constructor parameters.
// PASS: Use streams for transformations, keep pipelines short
List<String> names = markets.stream()
.map(Market::name)
.filter(Objects::nonNull)
.toList();
// PASS: extract a complex pipeline into a named method rather than reverting to a loopPrefer explicit type names over var. A narrow allowance remains for an obvious right-hand-side new expression where the type is already on the line (e.g., var list = new ArrayList<String>()), but the default is an explicit type.
Never use fully qualified class names in code; use imports.
MarketNotFoundException)catch (Exception ex) unless rethrowing/logging centrally@ControllerAdvice in Spring)throw new MarketNotFoundException(slug);public <T extends Identifiable> Map<Long, T> indexById(Collection<T> items) { ... }@NonNull / @Nullable (jspecify or jakarta.annotation)Optional as return type ONLY: not for fields or constructor parameters@Nullable only when unavoidable; otherwise use @NonNull@NotNull, @NotBlank) on inputsUse SLF4J as the logging API in all code; never import a concrete logging framework (Logback, Log4j2) directly in business logic.
Use @Slf4j (Lombok) for logger injection; never instantiate LoggerFactory.getLogger(...) manually.
// PASS
@Slf4j
public class MarketService {
public Market findBySlug(String slug) {
log.info("fetch_market slug={}", slug);
log.error("failed_fetch_market slug={}", slug, ex);
}
}
// FAIL
private static final Logger log = LoggerFactory.getLogger(MarketService.class);Log levels:
ERROR: unhandled exceptionsWARN: recoverable issuesINFO: significant domain eventsDEBUG: diagnostic detail#### Logging (Production)
logstash-logback-encoder for structured JSON logs in production#### Virtual Threads (Java 21+)
Use Executors.newVirtualThreadPerTaskExecutor() for all I/O-bound concurrency. Never use platform threads for I/O-bound work.
#### Async Pipelines
Use CompletableFuture for composing async pipelines. Always specify an explicit executor, never use the default ForkJoinPool for I/O:
CompletableFuture.supplyAsync(() -> fetchData(), ioExecutor)
.thenApplyAsync(data -> transform(data), computeExecutor);Avoid shared mutable state; use immutable Records or @Value classes as data carriers between threads. Document thread-safety guarantees (or lack thereof) on every class that is shared across threads.
JavaTimeModule globally for Java 8 date/time types; never configure per-object-mapper ad hocFAIL_ON_UNKNOWN_PROPERTIES to false for inbound DTOs (tolerant reader pattern)@JsonProperty for explicit field mapping, decoupling JSON keys from Java field namesBind configuration to typed, validated @ConfigurationProperties objects. Do not read environment variables or @Value placeholders directly throughout the code; centralize them in a properties class and validate it with Bean Validation annotations so misconfiguration fails fast at startup.
@Validated
@ConfigurationProperties(prefix = "market")
public record MarketProperties(@NotBlank String baseUrl, @Positive int maxPageSize) {}Prefer a compile-time mapper such as MapStruct for entity to DTO and DTO to entity conversion. Hand-write a mapper only where the library is awkward, for example when generated mapping would overwrite managed audit fields. A compile-time mapper keeps the conversion explicit, fast, and verified at build time rather than via reflection.
src/main/java/com/example/app/
config/
controller/
service/
repository/
domain/
dto/
util/
src/main/resources/
application.yml
src/test/java/... (mirrors main)Enforce in CI via Gradle:
HIGH and MEDIUM findings as errorsplugins {
id 'checkstyle'
id 'com.github.spotbugs' version '6.0.9'
id 'pmd'
}
checkstyle { toolVersion = '10.12.4'; ignoreFailures = false }
spotbugs { effort = 'max'; reportLevel = 'medium' }
pmd { ignoreFailures = false; ruleSetFiles = files('config/pmd/ruleset.xml') }Redirect Gradle output to a log file to prevent terminal overflow:
./gradlew <task> > build_log.txt 2>&1This overwrites build_log.txt so it always reflects only the most recent execution.
Use Javadoc only when strictly necessary to document a public class's non-obvious contract. Do NOT add Javadoc to methods, constructors, or self-explanatory classes.
@InjectMocks and @Mock instead of manual @BeforeEach initializationTestDataFactory classRemember: Keep code intentional, typed, and observable. Optimize for maintainability over micro-optimizations unless proven necessary.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.