Skip to content
Skill · GitHub Copilot.github/copilot-instructions.mdCoding

Copilot Instructions for Java Enterprise Services

Java service conventions for Copilot: immutable-by-default classes, Optional only as a return type, JUnit 5 with AssertJ, and exceptions that carry context instead of being swallowed.

Use when: Use in a Java 17+ backend service when you want Copilot to produce modern, immutable, well-tested Java instead of setter-heavy beans and bare try/catch blocks.

@libraryNewcomer
0 upvotes0 forks0 comments

The skill file

GitHub Copilot · .github/copilot-instructions.md
.github/copilot-instructions.md2948 chars
# Java Service Instructions ## Immutability - Default every field to `private final`; introduce mutability only with a comment explaining why. - Data carriers are `record`s. Do not generate JavaBean classes with getters/setters for new value types — records or builders only. - Collections exposed from any class are unmodifiable: return `List.copyOf(...)` or wrap with `Collections.unmodifiableList(...)`; never expose the internal list. - No setters on entities for fields that are part of the entity's identity or invariants — use intention-revealing methods (`order.markShipped(clock.instant())`). - Static mutable state is forbidden, including "caches" implemented as static `HashMap`s. Use an injected cache abstraction. ## Optional usage - `Optional` is a return type only. Never use it for fields, method parameters, or collection elements. - Never call `.get()` without `.isPresent()` — and prefer `orElseThrow(() -> new SpecificException(...))`, `map`, or `ifPresent` over presence checks entirely. - Do not return `Optional<Collection<T>>` — return an empty collection. - Repository finders for a single row return `Optional<T>`; service methods that require the row to exist resolve it immediately with `orElseThrow` and a domain exception. ## Error handling - Never catch `Exception` or `Throwable` except at the outermost boundary (request filter, message-consumer loop), and there, log with full stack trace and context fields. - Empty catch blocks are forbidden. If an exception is genuinely ignorable, log it at DEBUG with a comment saying why. - Wrap-and-rethrow must preserve the cause: `throw new PaymentFailedException("charge " + chargeId, e)` — never drop `e`. - Domain exceptions extend a small sealed hierarchy under `exception/`; do not invent a new top-level exception per class. - Log or rethrow, never both — double-logging the same failure at multiple layers is a bug. ## JUnit 5 patterns - JUnit 5 (`@Test` from `org.junit.jupiter`) with AssertJ assertions (`assertThat(...)`). Hamcrest and JUnit 4 are forbidden. - Test method names are sentences in camelCase: `refundFailsWhenOrderAlreadyRefunded()`. No `test` prefix. - Use `@ParameterizedTest` with `@CsvSource`/`@MethodSource` for input matrices instead of duplicated tests. - Exception assertions use `assertThatThrownBy(...).isInstanceOf(...).hasMessageContaining(...)` — never try/catch/fail in tests. - Inject a fixed `Clock` everywhere time is read; tests must never call `Instant.now()` or sleep. - Mock with Mockito only at I/O boundaries; construct real objects for everything in the same module. ## General - Streams over loops when the operation is a clean map/filter/collect; loops when there is branching, early exit, or checked exceptions involved. - No Lombok in new code; records and explicit constructors cover the need. - Run `./mvnw verify` (Checkstyle + tests) before suggesting any commit; never commit with `@Disabled` tests added.

Install

drop into your repo

Save this to your project or home directory so GitHub Copilot can load it.

path./.github/copilot-instructions.md
Discussion

What people are saying

Forks

0 forks of this skill
Loading forks…