java-coding-standards — Claude Code Java skill: setup
This skill applies Java conventions to Spring Boot and Quarkus code. Use it when an agent writes or reviews services built with either framework.
npx skills add affaan-m/ECC --skill java-coding-standards -a claude-codeWhat java-coding-standards does
The skill covers naming, immutability, Optional, streams, exceptions, and generics. It checks the build file first: quarkus selects Quarkus conventions, spring-boot selects Spring, and projects without either marker get the shared rules.
Spring guidance covers constructor injection and REST controllers named with the Controller suffix. Quarkus sections cover CDI, Panache, and reactive Uni/Multi pipelines; blocking calls inside those pipelines are flagged. The examples also address exception handling and project layout. The standards target Java 17+.
Install it for agents working on Spring Boot or Quarkus Java code. On projects using another framework, only the shared conventions apply.
What it helps with
- select Java conventions from the build file
- review
Optionaland stream usage - replace Spring field injection with constructor injection
- spot blocking calls in Quarkus reactive pipelines
- align domain exception handling with the framework
Example prompts for the agent
- Review this Spring Boot service against java-coding-standards and suggest changes.
- Check this Quarkus reactive pipeline for blocking calls.
- Rewrite MarketRepository injection using the skill's constructor injection rules.
How to install java-coding-standards
1Skills CLI (Vercel)
npx skills add affaan-m/ECC --skill java-coding-standards -a claude-code2Manually: put the files in place
~/.claude/skills/java-coding-standards/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/java-coding-standards/
1Skills CLI (Vercel)
npx skills add affaan-m/ECC --skill java-coding-standards -a codex2Manually: put the files in place
.agents/skills/java-coding-standards/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add affaan-m/ECC --skill java-coding-standards -a cursor2Manually: put the files in place
.cursor/skills/java-coding-standards/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install affaan-m/ECC java-coding-standards2Manually: put the files in place
.github/skills/java-coding-standards/SKILL.md1Skills CLI (Vercel)
npx skills add affaan-m/ECC --skill java-coding-standards -a gemini-cli2Platform CLI
gemini skills install https://github.com/affaan-m/ECC --path skills/java-coding-standardsManually: put the files in place
.devin/skills/java-coding-standards/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add affaan-m/ECC --skill java-coding-standards -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
From the same set31
The whole set →Similar
Questions about java-coding-standards
How do I install java-coding-standards in Claude Code?
Run npx skills add affaan-m/ECC --skill java-coding-standards -a claude-code.
What does java-coding-standards do?
It provides Java conventions for Spring Boot and Quarkus, covering areas such as Optional, dependency injection, exceptions, and reactive code.
How does the skill choose between Spring Boot and Quarkus rules?
It checks the build file. The quarkus marker selects Quarkus conventions, while spring-boot selects Spring; otherwise, it uses shared conventions.
Source text
SKILL.md · MIT
---
name: java-coding-standards
description: "Java coding standards for Spring Boot and Quarkus services: naming, immutability, Optional usage, streams, exceptions, generics, CDI, reactive patterns, and project layout. Automatically applies framework-specific conventions. Use when writing or reviewing Java in a Spring Boot or Quarkus service."
metadata:
origin: ECC
---
# Java Coding Standards
Standards for readable, maintainable Java (17+) code in Spring Boot and Quarkus services.
## When to Use
- Writing or reviewing Java code in Spring Boot or Quarkus projects
- Enforcing naming, immutability, or exception handling conventions
- Working with records, sealed classes, or pattern matching (Java 17+)
- Reviewing use of Optional, streams, or generics
- Structuring packages and project layout
- **[QUARKUS]**: Working with CDI scopes, Panache entities, or reactive pipelines
## How It Works
### Framework Detection
Before applying standards, determine the framework from the build file:
- Build file contains `quarkus` → apply **[QUARKUS]** conventions
- Build file contains `spring-boot` → apply **[SPRING]** conventions
- Neither detected → apply shared conventions only
## Core Principles
- Prefer clarity over cleverness
- Immutable by default; minimize shared mutable state
- Fail fast with meaningful exceptions
- Consistent naming and package structure
- **[QUARKUS]**: Favor build-time over runtime processing; avoid runtime reflection where possible
## Examples
The sections below show concrete Spring Boot, Quarkus, and shared Java examples
for naming, immutability, dependency injection, reactive code, exceptions,
project layout, logging, configuration, and tests.
## Naming
```java
// 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;
// PASS: [QUARKUS] JAX-RS resources named as *Resource, not *Controller
public class MarketResource {}
// PASS: [SPRING] REST controllers named as *Controller
public class MarketController {}
```
## Immutability
```java
// 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: [QUARKUS] Panache active-record entities use public fields (Quarkus convention)
@Entity
public class Market extends PanacheEntity {
public String name;
public MarketStatus status;
// Panache generates accessors at build time; public fields are idiomatic here
}
// PASS: [QUARKUS] Panache MongoDB entities
@MongoEntity(collection = "markets")
public class Market extends PanacheMongoEntity {
public String name;
public MarketStatus status;
}
```
## Optional Usage
```java
// PASS: Return Optional from find* methods
// [SPRING]
Optional<Market> market = marketRepository.findBySlug(slug);
// [QUARKUS] Panache
Optional<Market> market = Market.find("slug", slug).firstResultOptional();
// PASS: Map/flatMap instead of get()
return market
.map(MarketResponse::from)
.orElseThrow(() -> new EntityNotFoundException("Market not found"));
```
## Streams Best Practices
```java
// PASS: Use streams for transformations, keep pipelines short
List<String> names = markets.stream()
.map(Market::name)
.filter(Objects::nonNull)
.toList();
// FAIL: Avoid complex nested streams; prefer loops for clarity
```
## Dependency Injection
```java
// PASS: [SPRING] Constructor injection (preferred over @Autowired on fields)
@Service
public class MarketService {
private final MarketRepository marketRepository;
public MarketService(MarketRepository marketRepository) {
this.marketRepository = marketRepository;
}
}
// PASS: [QUARKUS] Constructor injection
@ApplicationScoped
public class MarketService {
private final MarketRepository marketRepository;
@Inject
public MarketService(MarketRepository marketRepository) {
this.marketRepository = marketRepository;
}
}
// PASS: [QUARKUS] Package-private field injection (acceptable in Quarkus — avoids proxy issues)
@ApplicationScoped
public class MarketService {
@Inject
MarketRepository marketRepository;
}
// FAIL: [SPRING] Field injection with @Autowired
@Autowired
private MarketRepository marketRepository; // use constructor injection
// FAIL: [QUARKUS] @Singleton when interception or lazy init is needed
@Singleton // non-proxyable — use @ApplicationScoped instead
public class MarketService {}
```
## Reactive Patterns [QUARKUS]
```java
// PASS: Return Uni/Multi from reactive endpoints
@GET
@Path("/{slug}")
public Uni<Market> findBySlug(@PathParam("slug") String slug) {
return Market.find("slug", slug)
.<Market>firstResult()
.onItem().ifNull().failWith(() -> new MarketNotFoundException(slug));
}
// PASS: Non-blocking pipeline composition
public Uni<OrderConfirmation> placeOrder(OrderRequest req) {
return validateOrder(req)
.chain(valid -> persistOrder(valid))
.chain(order -> notifyFulfillment(order));
}
// FAIL: Blocking call inside a Uni/Multi pipeline
public Uni<Market> find(String slug) {
Market m = Market.find("slug", slug).firstResult(); // BLOCKING — breaks event loop
return Uni.createFrom().item(m);
}
// FAIL: Subscribing more than once to a shared Uni
Uni<Market> shared = fetchMarket(slug);
shared.subscribe().with(m -> log(m));
shared.subscribe().with(m -> cache(m)); // double subscribe — use Uni.memoize()
```
## Exceptions
- Use unchecked exceptions for domain errors; wrap technical exceptions with context
- Create domain-specific exceptions (e.g., `MarketNotFoundException`)
- Avoid broad `catch (Exception ex)` unless rethrowing/logging centrally
```java
throw new MarketNotFoundException(slug);
```
### Centralised Exception Handling
```java
// [SPRING]
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MarketNotFoundException.class)
public ResponseEntity<ErrorResponse> handle(MarketNotFoundException ex) {
return ResponseEntity.status(404).body(ErrorResponse.from(ex));
}
}
// [QUARKUS] Option A: ExceptionMapper
@Provider
public class MarketNotFoundMapper implements ExceptionMapper<MarketNotFoundException> {
@Override
public Response toResponse(MarketNotFoundException ex) {
return Response.status(404).entity(ErrorResponse.from(ex)).build();
}
}
// [QUARKUS] Option B: @ServerExceptionMapper (RESTEasy Reactive)
@ServerExceptionMapper
public RestResponse<ErrorResponse> handle(MarketNotFoundException ex) {
return RestResponse.status(Status.NOT_FOUND, ErrorResponse.from(ex));
}
```
## Generics and Type Safety
- Avoid raw types; declare generic parameters
- Prefer bounded generics for reusable utilities
```java
public <T extends Identifiable> Map<Long, T> indexById(Collection<T> items) { ... }
```
## Project Structure
### [SPRING] Maven/Gradle
```
src/main/java/com/example/app/
config/
controller/
service/
repository/
domain/
dto/
util/
src/main/resources/
application.yml
src/test/java/... (mirrors main)
```
### [QUARKUS] Maven/Gradle
```
src/main/java/com/example/app/
config/ # @ConfigMapping, @ConfigProperty beans, Producers
resource/ # JAX-RS resources (not "controller")
service/
repository/ # PanacheRepository implementations (if not using active record)
domain/ # JPA/Panache entities, MongoDB entities
dto/
util/
mapper/ # MapStruct mappers (if used)
src/main/resources/
application.properties # Quarkus convention (YAML supported with quarkus-config-yaml)
import.sql # Hibernate auto-import for dev/test
src/test/java/... (mirrors main)
```
## Formatting and Style
- Use 2 or 4 spaces consistently (project standard)
- One public top-level type per file
- Keep methods short and focused; extract helpers
- Order members: constants, fields, constructors, public methods, protected, private
## Code Smells to Avoid
- Long parameter lists → use DTO/builders
- Deep nesting → early returns
- Magic numbers → named constants
- Static mutable state → prefer dependency injection
- Silent catch blocks → log and act or rethrow
- **[QUARKUS]**: `@Singleton` where `@ApplicationScoped` is intended — breaks proxying and interception
- **[QUARKUS]**: Mixing `quarkus-resteasy-reactive` and `quarkus-resteasy` (classic) — pick one stack
- **[QUARKUS]**: Panache active-record + repository pattern in the same bounded context — pick one
## Logging
```java
// [SPRING] SLF4J
private static final Logger log = LoggerFactory.getLogger(MarketService.class);
log.info("fetch_market slug={}", slug);
log.error("failed_fetch_market slug={}", slug, ex);
// [QUARKUS] JBoss Logging (default, zero-cost at build time)
private static final Logger log = Logger.getLogger(MarketService.class);
log.infof("fetch_market slug=%s", slug);
log.errorf(ex, "failed_fetch_market slug=%s", slug);
// [QUARKUS] Alternative: simplified logging with @Inject
@Inject
Logger log; // CDI-injected, scoped to declaring class
```
## Null Handling
- Accept `@Nullable` only when unavoidable; otherwise use `@NonNull`
- Use Bean Validation (`@NotNull`, `@NotBlank`) on inputs
- **[QUARKUS]**: Apply `@Valid` on `@BeanParam`, `@RestForm`, and request body parameters
## Configuration
```java
// [SPRING] @ConfigurationProperties
@ConfigurationProperties(prefix = "market")
public record MarketProperties(int maxPageSize, Duration cacheTtl) {}
// [QUARKUS] @ConfigMapping (type-safe, build-time validated)
@ConfigMapping(prefix = "market")
public interface MarketConfig {
int maxPageSize();
Duration cacheTtl();
}
// [QUARKUS] Simple values with @ConfigProperty
@ConfigProperty(name = "market.max-page-size", defaultValue = "100")
int maxPageSize;
```
## Testing Expectations
### Shared
- JUnit 5 + AssertJ for fluent assertions
- Mockito for mocking; avoid partial mocks where possible
- Favor deterministic tests; no hidden sleeps
### [SPRING]
- `@WebMvcTest` for controller slices, `@DataJpaTest` for repository slices
- `@SpringBootTest` reserved for full integration tests
- `@MockBean` for replacing beans in Spring context
### [QUARKUS]
- Plain JUnit 5 + Mockito for unit tests (no `@QuarkusTest`)
- `@QuarkusTest` reserved for CDI integration tests
- `@InjectMock` for replacing CDI beans in integration tests
- Dev Services for database/Kafka/Redis — avoid manual Testcontainers setup when Dev Services suffice
- `@QuarkusTestResource` for custom external service lifecycle
```java
// [SPRING] Controller test
@WebMvcTest(MarketController.class)
class MarketControllerTest {
@Autowired MockMvc mockMvc;
@MockBean MarketService marketService;
}
// [QUARKUS] Integration test
@QuarkusTest
class MarketResourceTest {
@InjectMock
MarketService marketService;
@Test
void should_return_404_when_market_not_found() {
given().when().get("/markets/unknown").then().statusCode(404);
}
}
// [QUARKUS] Unit test (no CDI, no @QuarkusTest)
@ExtendWith(MockitoExtension.class)
class MarketServiceTest {
@Mock MarketRepository marketRepository;
@InjectMocks MarketService marketService;
}
```
**Remember**: Keep code intentional, typed, and observable. Optimize for maintainability over micro-optimizations unless proven necessary.
© affaan-m · MIT · affaan-m/ECC