Java style rules
Standing rules for Java an assistant writes, covering modern language features, immutability, Optional and null handling, exceptions, restrained streams, records and JUnit 5 tests.
When you write or change Java code in this project:
Tooling and version
- Use the Java version the build declares (
maven.compiler.release, the Gradle toolchain) and only language features it supports. Do not raise the version on your own. - Follow the project's formatter and static analysis (Spotless, google-java-format, Checkstyle, Error Prone, SpotBugs) and keep the build free of new warnings.
- Do not add a dependency for something the JDK does in a few lines; when one is needed, add it through the build file with an explicit version or the project's version catalog or BOM.
Modern language features
- Use records for immutable data carriers, sealed interfaces for closed hierarchies, switch expressions and pattern matching (
instanceofpatterns, record patterns where available) instead ofinstanceof-and-cast chains, and text blocks for multi-line strings. - Use
varonly when the type is obvious from the right-hand side. Keep explicit types on fields, parameters and return types. - Use
java.timefor all dates and times (Instantfor timestamps,LocalDatefor calendar dates, aClockinjected where code needs "now"). Neverjava.util.DateorCalendarin new code. - Use
BigDecimalfor money with an explicitRoundingMode, and compare it withcompareTo, notequals.
Immutability
- Make fields
finalby default and classes immutable where practical. ReturnList.copyOf,Map.copyOfor unmodifiable views, never internal mutable collections. - Prefer static factory methods or builders over constructors with many parameters of the same type.
Null handling and Optional
- Do not return
nullfor collections or arrays; return empty ones. - Use
Optionalonly as a return type for "may be absent". Never as a field, parameter or collection element, and never callOptional.get(); useorElseThrow,orElse,maporifPresent. - Validate arguments at public boundaries with
Objects.requireNonNull(value, "name"). Follow the project's nullness annotations (for example JSpecify@Nullableand@NullMarked) if it uses them. - Compare strings with
equals, putting the constant or non-null side first, never with==.
Exceptions
- Throw specific exceptions with a message that includes the offending value. Use unchecked exceptions for programming errors and checked exceptions only where the caller can actually recover.
- Never swallow an exception. When wrapping, pass the cause. Do not catch
ExceptionorThrowableexcept at a top-level boundary that logs and translates. - Close resources with try-with-resources. Do not use exceptions for normal control flow.
Streams and collections
- Use streams for clear transformations (filter, map, collect). Use a plain loop when the stream would need nested lambdas, checked exceptions, index juggling or side effects.
- No side effects inside stream operations except in
forEachat the end. Do not useparallelStream()without a measurement showing it helps. - Implement
equalsandhashCodetogether (records do this for you), and never mutate an object while it is a key in a map or a member of a set.
Concurrency
- Prefer
java.util.concurrenttypes and executors over raw threads, and shut executors down (try-with-resources onExecutorServicewhere the Java version allows). - Share only immutable state between threads, or guard it with a single, documented mechanism. Use virtual threads only if the project already does. Before Java 24, a blocking call inside
synchronizedpins the carrier thread, so guard such sections with aReentrantLockinstead; do not pool virtual threads, and limit concurrency to scarce resources with aSemaphore.
Logging
- Use the project's logging facade (usually SLF4J) with parameterised messages:
log.info("Order {} shipped", orderId). NeverSystem.out, string concatenation in log calls, or logging secrets and personal data.
Tests (JUnit 5)
- Use JUnit Jupiter:
@Test,@ParameterizedTestwith@CsvSourceor@MethodSourcefor input tables,@Nestedto group cases, andassertThrowsfor expected exceptions, checking the message or type. - Use the project's assertion library (AssertJ or JUnit assertions) consistently. One behaviour per test, named for it.
- Mock only at system boundaries (HTTP clients, repositories, clocks), never the class under test. Inject a fixed
Clockinstead of mocking static time. - No
Thread.sleepto wait for asynchronous work; use the project's awaiting utility (such as Awaitility) or synchronise explicitly.
details
- kind
- Rule: standing instructions for everything the assistant does
- domain
- Software engineering
- category
- Conventions
- made for
- Software engineer, Backend engineer
- risk
- read-only
- version
- v1.0.0 · experimental
- reviewed
- 2026-10-02
- works in
- Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Antigravity, OpenCode, Windsurf, Zed, Continue, AGENTS.md, ChatGPT, claude.ai
use in
npx @hermes-hq/hodios install java-style-rules --target claude-codenpx skills add hermes-hq/hodios-dist --skill java-style-rules -a claude-codeRules are always-on instructions, so they are not in the plugins: add the skill, or paste the text into CLAUDE.md.
pairs well with
All of ConventionsTest-writing rules
Standing rules for tests an assistant writes, covering behaviour over implementation, no sleeps, deterministic data, mocks only at boundaries and one reason to fail per test.
test-writing-rulesSecure coding rules
Makes the assistant write code that validates untrusted input, avoids injection, protects secrets and checks authorization by default. Use as always-on rules in any codebase.
secure-coding-rulesHTTP API design rules
Rules for HTTP APIs covering resource naming, status codes, problem+json errors, cursor pagination, idempotency keys and versioning. Load when designing or changing HTTP endpoints.
api-design-rulesC# style rules
Standing rules for C# an assistant writes, covering nullable reference types, async all the way with cancellation tokens, records and pattern matching, dependency injection and xUnit tests.
csharp-style-rulesGo style rules
Standing rules for Go an assistant writes, covering wrapped errors, context propagation, small consumer-side interfaces, table-driven tests and no goroutines without an owner.
go-style-rulesPython style rules
Standing rules for Python an assistant writes, covering type hints, pathlib, logging over print, explicit exceptions, safe subprocess calls, project layout and the project's own tooling.
python-style-rules