Add this file to your project root so Claude uses correct commands every session — no guessing aliases or build tool flags.
# Project
Java 21 Spring Boot 3.3 REST API (Maven multi-module)
# Build
./mvnw clean package -DskipTests # full build, skip tests
./mvnw test # all tests
./mvnw test -pl service-module # tests for one module
./mvnw spring-boot:run # dev server on :8080
./mvnw spotless:apply # auto-format (Google Java Style)
./mvnw spotless:check # CI lint check
# Key conventions
- Constructor injection only — never @Autowired on fields
- MapStruct for DTO mapping — never ModelMapper or manual mappers
- Spring Data JPA with Specifications for dynamic queries
- @Transactional on service layer, never on repositories
- Java records for DTOs and value objects (Java 21)
- Use switch expressions, pattern matching instanceof, text blocks
- AssertJ for test assertions — never assertEquals
# Do not modify
target/ .mvn/ generated-sources/
Gradle projects: Replace ./mvnw with ./gradlew and adjust commands — e.g., ./gradlew test, ./gradlew bootRun. Claude understands both DSLs equally well.
Project Setup Prompts
Maven
Gradle
Multi-Module
Maven pom.xml Setup
Update pom.xml: add Spring Boot 3.3 parent, Java 21 source/target, spring-boot-starter-web, spring-boot-starter-data-jpa, spring-boot-starter-security, postgresql driver, lombok, mapstruct 1.6, and springdoc-openapi-starter-webmvc-ui. Add Spotless plugin configured for Google Java Style. Add Surefire plugin with failsafe for integration tests.
Application Properties
Create src/main/resources/application.yml with Spring Boot 3 configuration: datasource with HikariCP pool settings, JPA with Hibernate dialect auto-detect, show_sql: false, ddl-auto: validate, Flyway migration enabled, logging levels for SQL in debug profile, and a separate application-dev.yml that enables h2 in-memory database for local development.
Checkstyle / Spotless Config
Add Spotless Maven plugin to pom.xml configured for Google Java Style with import ordering. Create spotless.xml and import order file. Add a pre-commit hook (in .git/hooks/pre-commit) that runs ./mvnw spotless:check and blocks the commit if it fails. Add spotless:apply to the README quick-start.
Gradle Kotlin DSL Setup
Set up build.gradle.kts: Spring Boot 3.3 plugin, java 21 toolchain, spring-boot-starter-web, data-jpa, security, postgresql, lombok, mapstruct 1.6. Add the Spotless plugin (ktlint for build scripts, google-java-format for src). Configure test to use JUnit 5 platform. Add a buildSrc/ convention plugin that all subprojects apply.
Gradle Wrapper Init
Generate a Gradle wrapper pinned to Gradle 8.8 using gradle wrapper --gradle-version 8.8. Then add .gitattributes that marks gradlew as executable so it doesn't need chmod on fresh checkouts. Verify ./gradlew tasks runs without errors.
Maven Multi-Module Layout
Restructure this project into a Maven multi-module layout: parent pom, domain/ (entities + repositories), service/ (business logic, depends on domain), api/ (Spring MVC controllers + DTOs + MapStruct mappers, depends on service), and infrastructure/ (external HTTP clients, messaging adapters). Move existing code to the correct modules. Keep a single spring-boot:run target in api/.
Shared Dependency Management
Add a dependencyManagement section to the parent pom that centralises all versions (spring-boot BOM, mapstruct, lombok, testcontainers, rest-assured). Remove version tags from all child pom.xml files. Ensure no module pins a version that conflicts with the BOM. Run ./mvnw dependency:tree to verify there are no version conflicts.
Spring Boot Patterns
REST Controller + Validation
Create a Spring Boot REST controller for /api/v1/products with endpoints: GET / (paginated list with @RequestParam filters), GET /{id}, POST / (create with @Valid + @RequestBody), PUT /{id} (full update), PATCH /{id} (partial update), DELETE /{id}. Use records for request/response DTOs. Add Bean Validation annotations. Return 201 with Location header on create, 204 on delete.
Global Exception Handler
Add a @RestControllerAdvice that handles: MethodArgumentNotValidException (400, list all field errors as [{field, message}]), EntityNotFoundException (404 with resource name), DataIntegrityViolationException (409 with constraint name), and Exception fallthrough (500). Use a consistent ProblemDetail response (RFC 9457 / Spring 6 built-in). Add @Slf4j and log stack traces at ERROR for 5xx only.
Spring Security JWT Setup
Add Spring Security with stateless JWT authentication: SecurityFilterChain bean, JwtAuthenticationFilter extending OncePerRequestFilter that reads the Authorization header, validates the token with JJWT (io.jsonwebtoken), extracts the username and roles, and sets SecurityContextHolder. Permit /auth/**, /actuator/health. Add JwtService with generateToken() and validateToken(). No sessions — STATELESS policy.
Spring Data JPA + Specifications
Write a Spring Data JPA repository for Order that supports filtering by: status (enum), createdAt date range, customerId, and totalAmount min/max. Use JpaSpecificationExecutor with a static OrderSpecs helper class — one method per filter, composable with and(). Add a findAll(Specification, Pageable) call in the service. Write @DataJpaTest tests for each specification using H2.
Flyway Database Migrations
Add Flyway to this Spring Boot project. Create V1__init_schema.sql with the current schema (infer from existing @Entity classes). Create V2__add_indexes.sql adding indexes on foreign keys and frequently-queried columns. Configure Flyway to run only in non-test profiles (skip in @SpringBootTest). Add a Flyway repair step to the README for fixing failed migrations in production.
MapStruct DTO Mapping
Add MapStruct mappers for all entities in the domain package. For each entity create: EntityDto (read-only record), CreateEntityRequest, UpdateEntityRequest. Generate toDto(), fromCreateRequest(), and applyUpdate(entity, request) methods. Use @Mapping(target="id", ignore=true) on create. Add @BeanMapping(nullValuePropertyMappingStrategy=IGNORE) on partial update. Verify compilation with ./mvnw compile.
Spring Batch Job
Create a Spring Batch job that reads CSV files from an S3 bucket (using Spring Cloud AWS), validates each row, transforms it to a Product entity, and bulk-inserts via JdbcBatchItemWriter. Use a FlatFileItemReader with @StepScope and chunk-oriented processing (chunk size 500). Add a JobExecutionListener that emails on failure. Write a @SpringBatchTest integration test.
Java 21 Modernisation
Migrate POJOs → Records
Convert all DTO and value-object classes in src/main/java/com/example/dto/ to Java 21 records. Keep existing constructors as compact constructors where validation is needed. Update all usages (constructors replaced by record constructors, getters replaced by accessor methods). Run ./mvnw compile and ./mvnw test to verify nothing broke.
Pattern Matching instanceof
Replace all instanceof checks followed by explicit casts in src/ with Java 21 pattern matching instanceof. Also replace switch statements that switch on type (using getClass()) with sealed interface + switch expressions where the types form a closed set. Run ./mvnw test after each file.
Streams and Collectors Modernisation
Audit src/main/java/ for: (1) for-loops that can become stream pipelines, (2) manual null checks that can become Optional chains, (3) Collectors.groupingBy that can use the newer toUnmodifiableMap, (4) string concatenation in loops that should use StringJoiner or String.join. Apply the cleanest idiomatic Java 21 version. Preserve all existing behaviour — verify with existing tests.
Text Blocks for SQL
Find all multi-line string concatenation used for SQL queries in src/ and replace with Java 15+ text blocks. Ensure indentation aligns with the Java source (use \s or trailing space carefully). Also replace any large JSON strings in tests with text blocks. Run ./mvnw test to confirm queries still parse correctly.
Virtual Threads (Project Loom)
Enable Project Loom virtual threads in this Spring Boot 3.3 application: set spring.threads.virtual.enabled=true in application.yml. Identify any ThreadLocal usage that may not be compatible with virtual threads and replace with ScopedValue where needed. Add a @Bean Executor virtualThreadExecutor using Executors.newVirtualThreadPerTaskExecutor() for @Async tasks. Write a load test that spins up 10,000 concurrent requests to verify throughput improvement.
Testing Prompts
JUnit 5 + Mockito Unit Tests
Write JUnit 5 + Mockito unit tests for UserService. Cover: (1) createUser happy path — verify repository.save() called, email sent, DTO returned; (2) createUser with duplicate email — verify DataIntegrityViolationException rethrown as UserAlreadyExistsException; (3) getUserById not found — verify UserNotFoundException thrown; (4) updateUser — verify only changed fields written via partial update. Use @ExtendWith(MockitoExtension.class), @Mock, @InjectMocks, and AssertJ assertThat.
@WebMvcTest Controller Tests
Write @WebMvcTest tests for ProductController: GET / with pagination params returns 200 + page JSON; POST / with valid body returns 201 + Location header; POST / with invalid body returns 400 + field error list; DELETE /{id} for non-existent id returns 404. Use MockMvc, @MockBean for ProductService, and Jackson ObjectMapper for request bodies. Assert response JSON with jsonPath.
Testcontainers Integration Tests
Add Testcontainers to this project. Create an abstract BaseIntegrationTest class annotated @SpringBootTest(webEnvironment=RANDOM_PORT) that starts a PostgreSQL container (@Container static field, @DynamicPropertySource to wire the JDBC URL). Extend it in OrderRepositoryTest — write integration tests for the custom Specification queries that run against real PostgreSQL. Add testcontainers BOM to pom.xml.
@DataJpaTest Slice Tests
Write @DataJpaTest tests for ProductRepository that cover: findByStatus (verify correct count), findByPriceRange (verify boundary conditions), the custom JPQL query for low-stock (verify threshold logic), and the specification for multi-filter search. Use an in-memory H2 database with ddl-auto: create. Add @Sql to load test fixtures before each test class.
ArchUnit Architecture Tests
Add ArchUnit to the test dependencies. Write architecture tests that enforce: (1) controllers only depend on services, never repositories; (2) repositories only in the domain package; (3) no cycles between packages; (4) all @Service classes are in the service package; (5) all @RestController classes end in 'Controller'. Run these as part of the standard ./mvnw test phase so CI catches violations immediately.
Parameterized Tests
Convert the PricingServiceTest to use @ParameterizedTest with @MethodSource. The source method should return a Stream of Arguments with: input quantity, customer tier, expected discount percentage, and expected final price. Cover at least 8 scenarios including edge cases (quantity=0, premium tier, bulk discount threshold). Use BigDecimal for prices to avoid floating point errors.
Performance & Production Patterns
JPA N+1 Query Fix
Audit the Order entity and its relationships. Identify all N+1 query patterns: any @OneToMany or @ManyToOne relation that causes a query per row. Fix with: (1) JOIN FETCH in JPQL for specific use cases, (2) @EntityGraph on repository methods, (3) @BatchSize for collections that can't be eagerly loaded globally. Add Hibernate statistics logging temporarily to count queries before and after. Write a test that asserts order list loads in 1 query.
Spring Cache Abstraction
Add Spring Cache with Redis (spring-boot-starter-data-redis) to this application. Configure a CacheManager with TTLs: productCache=10min, categoryCache=1hour, userProfileCache=30min. Annotate service methods with @Cacheable, @CachePut on update, @CacheEvict on delete. Add @EnableCaching to the config. Write an integration test with EmbeddedRedis (or Testcontainers Redis) that verifies the cache is hit on the second call.
Docker Multi-Stage Build
Write a Dockerfile for this Spring Boot app using a multi-stage build: stage 1 uses eclipse-temurin:21-jdk-alpine to run ./mvnw package -DskipTests, stage 2 uses eclipse-temurin:21-jre-alpine and copies only the fat JAR. Add a non-root USER. Set JVM flags for containers: -XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0. Add a docker-compose.yml with the app + postgres + redis services for local dev. Add .dockerignore excluding target/ and .mvn/wrapper/maven-wrapper.jar.
Actuator + Micrometer Metrics
Enable Spring Boot Actuator with: health (show-details: when-authorized), info (build info via maven-git-commit-id-plugin), metrics, prometheus endpoint. Add Micrometer Prometheus registry. Create a custom @Component that registers a business metric: active_orders_count as a Gauge polling the database every 30 seconds. Configure security to permit /actuator/health and /actuator/prometheus but require ROLE_ADMIN for all other actuator endpoints.
Legacy Code Refactoring
Remove Boilerplate with Lombok
Add Lombok to this project. Replace all manual getter/setter pairs with @Getter/@Setter or @Data. Replace builder inner classes with @Builder. Replace log = LoggerFactory.getLogger(Foo.class) with @Slf4j. Skip @Data on any @Entity class — use @Getter @Setter only and keep the existing hashCode/equals based on id. Run ./mvnw compile to verify annotation processing works correctly.
Extract Service Layer from Controllers
Refactor: all business logic currently in the @RestController methods should be extracted into @Service classes. Controllers should only: validate input (delegating to Bean Validation), call the service, and map the result to a response. Each controller method should be ≤10 lines after refactoring. Move database calls out of controllers entirely. Add unit tests for the new service classes after extraction.
Replace Raw JDBC with Spring Data JPA
Migrate the DAO classes that use raw JdbcTemplate and ResultSet mapping to Spring Data JPA repositories. For each DAO: (1) create a @Entity for the table, (2) create a JpaRepository or CrudRepository interface, (3) convert custom SQL queries to @Query or derived method names, (4) move complex dynamic queries to JpaSpecificationExecutor. Keep the JdbcTemplate for batch insert paths where JPA performance is insufficient.
Microservices & Messaging
Spring Cloud OpenFeign Client
Add Spring Cloud OpenFeign to replace the RestTemplate calls in ExternalPaymentService. Create a @FeignClient for the payment gateway API: define the interface methods matching the endpoint signatures, add a FeignConfig with request logging at BASIC level, add a retry policy (3 attempts, 1s backoff) using Resilience4j, and add a fallback factory for graceful degradation. Write a @SpringBootTest test using WireMock to simulate the payment gateway.
Kafka Producer + Consumer
Add Spring Kafka to this service. Create a KafkaProducer @Service that publishes OrderCreatedEvent to 'orders.created' topic with the orderId as the key (ensures ordering per order). Create a @KafkaListener in a separate service that consumes this topic, processes events with at-least-once semantics, and handles DeserializationException by sending to a dead-letter topic. Add Testcontainers Kafka integration tests for both producer and consumer.
OpenAPI Documentation
Add springdoc-openapi-starter-webmvc-ui to this project. Annotate all @RestController endpoints with @Operation(summary=, description=), @ApiResponse for each HTTP status code, and @Parameter for query params. Add @Schema annotations to DTO records. Configure OpenAPI bean with API title, version, contact, and license. Enable swagger-ui at /swagger-ui.html in dev profile only. Verify the generated spec is valid with the OpenAPI parser.
CLAUDE.md Templates by Project Type
Spring Boot API
Spring Batch
Legacy Monolith
# Spring Boot REST API
Java 21, Spring Boot 3.3, PostgreSQL, Redis
## Commands
- Build: ./mvnw clean package -DskipTests
- Test: ./mvnw test
- Run: ./mvnw spring-boot:run -Dspring-boot.run.profiles=dev
- Lint: ./mvnw spotless:check
- Format: ./mvnw spotless:apply
## Conventions
- Constructor injection only (never @Autowired on fields)
- Records for DTOs, @Entity for JPA (never mix)
- MapStruct for all DTO mapping
- @Transactional on service methods, not repositories
- Custom exceptions extend RuntimeException with HTTP status annotation
- Feature flags via @ConditionalOnProperty
## Testing
- Unit: JUnit 5 + Mockito + AssertJ (./mvnw test -pl .)
- Integration: Testcontainers PostgreSQL in BaseIntegrationTest
- Slice: @WebMvcTest for controllers, @DataJpaTest for repos
- Architecture: ArchUnit rules in ArchitectureTest.java
## Off-limits
target/ .mvn/ src/main/generated/
# Spring Batch Processing Service
Java 21, Spring Boot 3.3, Spring Batch 5, PostgreSQL
## Commands
- Build: ./mvnw clean package
- Test: ./mvnw test (skips long-running job tests)
- Run job: ./mvnw spring-boot:run --args="--spring.batch.job.enabled=true --jobName=importProducts"
- Integration tests: ./mvnw verify -P integration-test
## Conventions
- Chunk size 500 for DB writes, 100 for API calls (rate limit aware)
- @StepScope for all readers/writers/processors (enables late binding)
- JobExecutionListener for notifications (Slack webhook)
- Dead letter table for failed rows: batch_failed_items
- Idempotent jobs: check job_execution for SUCCESS before running
## Do not modify
target/ job-logs/
# Legacy Monolith (Java 11, Spring Boot 2.7)
Gradual migration to Java 21 in progress
## Commands
- Build: mvn clean package (no wrapper yet — TODO)
- Test: mvn test -Dspring.profiles.active=test
- Run: java -jar target/app.jar --spring.profiles.active=dev
## Migration status
- auth package: migrated to Java 21 idioms ✓
- product package: in progress
- order package: legacy — do not refactor without asking
## Conventions (legacy — do not apply Java 21 patterns to legacy packages)
- Field injection used throughout legacy code — do not change without full class migration
- DAO pattern with JdbcTemplate in legacy code
- Java 21 packages: records, pattern matching, switch expressions OK
## Off-limits
src/main/webapp/WEB-INF/ target/ sql/migrations/
FAQ
Does Claude Code work with Maven and Gradle?
Yes. Claude Code inherits your shell's Java version and reads pom.xml or build.gradle to understand dependencies, plugins, and project structure. It understands Maven multi-module layouts and both Groovy and Kotlin DSL for Gradle. Add your build commands to CLAUDE.md so Claude runs them consistently.
How do I use Claude Code with Spring Boot?
Claude Code understands Spring Boot's auto-configuration, component scanning, DI annotations, Spring Data JPA, Spring Security, and the WebMvcTest/SpringBootTest/DataJpaTest slice-testing approach. Put your Spring Boot version and key starters in CLAUDE.md for best results.
How does Claude Code work with JUnit 5 and Mockito?
Claude writes idiomatic JUnit 5 tests: @ExtendWith(MockitoExtension.class), @Mock, @InjectMocks, @Captor, ArgumentMatchers, @Nested, @ParameterizedTest with @CsvSource/@MethodSource, and AssertJ fluent assertions. It generates realistic stubs and verifies interaction counts with verify().
Can Claude Code migrate Java 8 code to Java 21?
Yes — version migration is one of Claude Code's strongest use cases. It replaces anonymous inner classes with lambdas, converts POJOs to records, replaces switch statements with switch expressions, and introduces pattern matching instanceof. Say: 'Migrate src/main/java/ from Java 8 idioms to Java 21. Keep behaviour identical.' Commit each class migrated so you can roll back individually.
What should I put in CLAUDE.md for a Java project?
Include: Java version and how it's installed (SDKMAN, jenv), build tool + wrapper command (./mvnw or ./gradlew), test/lint/run commands, key dependency conventions (MapStruct not ModelMapper, constructor injection only), and directories Claude must not touch (target/, build/). Precise commands prevent Claude from guessing wrong aliases.
How do I use Claude Code for Spring Data JPA?
Claude understands the repository abstraction, derived query methods, @Query with JPQL and native SQL, Specifications, and Projections. It writes correct @Entity mappings including FetchType.LAZY to avoid N+1 queries, correct cascade types, and orphanRemoval. Prompt: 'Write a Spring Data JPA repository for Order that supports filtering by status and date range using a Specification.'
Can Claude Code help with Lombok?
Claude uses Lombok naturally if it's on the classpath: @Data, @Builder, @Value, @Slf4j, @RequiredArgsConstructor. It also knows when NOT to use Lombok — for example, it avoids @Data on @Entity classes because of Hibernate proxy issues, and instead uses explicit @Getter @Setter with an id-based equals/hashCode. Just mention 'Lombok is available' in your CLAUDE.md.