hermes

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 (instanceof patterns, record patterns where available) instead of instanceof-and-cast chains, and text blocks for multi-line strings.
  • Use var only when the type is obvious from the right-hand side. Keep explicit types on fields, parameters and return types.
  • Use java.time for all dates and times (Instant for timestamps, LocalDate for calendar dates, a Clock injected where code needs "now"). Never java.util.Date or Calendar in new code.
  • Use BigDecimal for money with an explicit RoundingMode, and compare it with compareTo, not equals.

Immutability

  • Make fields final by default and classes immutable where practical. Return List.copyOf, Map.copyOf or 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 null for collections or arrays; return empty ones.
  • Use Optional only as a return type for "may be absent". Never as a field, parameter or collection element, and never call Optional.get(); use orElseThrow, orElse, map or ifPresent.
  • Validate arguments at public boundaries with Objects.requireNonNull(value, "name"). Follow the project's nullness annotations (for example JSpecify @Nullable and @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 Exception or Throwable except 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 forEach at the end. Do not use parallelStream() without a measurement showing it helps.
  • Implement equals and hashCode together (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.concurrent types and executors over raw threads, and shut executors down (try-with-resources on ExecutorService where 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 synchronized pins the carrier thread, so guard such sections with a ReentrantLock instead; do not pool virtual threads, and limit concurrency to scarce resources with a Semaphore.

Logging

  • Use the project's logging facade (usually SLF4J) with parameterised messages: log.info("Order {} shipped", orderId). Never System.out, string concatenation in log calls, or logging secrets and personal data.

Tests (JUnit 5)

  • Use JUnit Jupiter: @Test, @ParameterizedTest with @CsvSource or @MethodSource for input tables, @Nested to group cases, and assertThrows for 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 Clock instead of mocking static time.
  • No Thread.sleep to 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

Edit on GitHubReport a problem

use in

Hodios CLI
npx @hermes-hq/hodios install java-style-rules --target claude-code
Agent Skills
npx skills add hermes-hq/hodios-dist --skill java-style-rules -a claude-code

Rules 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 Conventions
RuleTesting

Test-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-rules
RuleSecurity

Secure 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-rules
RuleConventions

HTTP 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-rules
RuleConventions

C# 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-rules
RuleConventions

Go 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-rules
RuleConventions

Python 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