Spring Boot: Best Practices for AI-Assisted Development
A practical guide for using Claude Code and GitHub Copilot effectively in Spring Boot projects, covering project setup, reusable skills, architecture patterns, and daily workflows.
Content Table
- Purpose
- Audience
- Prerequisites
- Setting Up Your Spring Boot Project for AI
- Spring Boot Official AI Resources
- AI Context Files for Spring Boot Projects
- AGENTS.md for Spring Boot Projects
- Reusable Skills for Spring Boot
- Architecture Patterns and Code Snippets
- Daily Workflows with AI Assistants
- Spring Boot Best Practices
- Specialized Best Practices
- Tips and Pitfalls
- References
Purpose
Spring Boot projects benefit greatly from AI-assisted development, but generic LLM output can produce suboptimal patterns (constructor injection mixed with field injection, @RestControllerAdvice forgotten, test slices misused). This guide solves that problem by configuring Claude Code and GitHub Copilot with Spring Boot-specific context for idiomatic code generation from the start.
By the end of this guide you will have:
- An AI context file tailored to your Spring Boot project
- Reusable task instructions for common Spring Boot tasks
- Reference architecture snippets that AI assistants can follow
- Practical workflows for daily Spring Boot development
Audience
- Spring Boot developers using AI assistants for development, refactoring, or testing.
- Teams standardizing AI usage across multiple Spring Boot codebases.
Prerequisites
- Java 17+ (21 recommended)
- Maven 3.8+ or Gradle 7.5+
- Spring Boot 3.2+
- Claude Code installed (enterprise access recommended)
- GitHub Copilot enabled in VS Code/IntelliJ
- A working Spring Boot project (new or existing)
Setting Up Your Spring Boot Project for AI
Configure these layers of context before writing prompts.
Layer 1: Project Memory with AGENTS.md
Every modern AI coding assistant supports a project-level context file that provides persistent instructions. This file tells the assistant about your project structure, conventions, and constraints.
AGENTS.md at project root provides persistent context for every session giving project-specific guidance for code completion, chat, and reviews.
See full example: snippets/AGENTS.md
AI Labs: Reusable Skills
Several AI assistants now support a skills concept — reusable Markdown files with structured instructions that the assistant can invoke for specific tasks.
| AI Assistant | Skills Location | File Format | Docs |
|---|---|---|---|
| Claude Code | .claude/skills/<name>/SKILL.md | Markdown with YAML frontmatter | Claude Code Skills |
| Cursor | .cursor/skills/<name>/SKILL.md | Markdown with YAML frontmatter | Cursor Skills |
| GitHub Copilot | .github/skills/<name>.md or .claude/skills/<name>/SKILL.md | Markdown with YAML frontmatter | Copilot Skills |
All three tools use directory-based skills centered on a SKILL.md file with YAML frontmatter and free-form Markdown instructions, so most basic syntax and structure is shared; they use the same open format, making skills directly portable between them.
Store skills in ./skills/<skill-name>/SKILL.md. Ready-to-use skills in snippets/.
Spring Boot Official AI Resources
| Resource | URL | Usage |
|---|---|---|
| Spring AI Reference | docs.spring.io/spring-ai | AI integration patterns |
| Spring Boot AI Guide | spring.io/blog/spring-ai | Official AI development guidance |
| Spring REST Docs | spring.io/projects/spring-restdocs | API documentation patterns |
| SpringDoc OpenAPI | springdoc.org | Auto-generated API docs |
AI Context Files for Spring Boot Projects
A well-crafted context file is the single most impactful thing you can do for AI-assisted development. It prevents AI assistants from generating outdated patterns and ensures consistency across the project. The content below applies regardless of which tool you use — only the file name and location differ.
| AI Assistant | Context File | Location |
|---|---|---|
| Claude Code | CLAUDE.md | Project root or .claude/ |
| GitHub Copilot | copilot-instructions.md | .github/ |
| Cursor | .cursorrules | Project root |
AGENTS.md for Spring Boot Projects
Use AGENTS.md as the persistent project memory for sessions.
See full example: snippets/AGENTS.md
Architecture Patterns and Code Snippets
Reference architecture snippets that AI assistants can follow.
See snippets: snippets/
Daily Workflows with AI Assistants
Generating New Feature
/spring-controller OrderController --feature orders
/spring-service OrderService
/spring-test OrderController.javaPrompt example:
Create "orders" feature following CLAUDE.md patterns:
- OrderController with POST /api/v1/orders
- CreateOrderRequest DTO with @Valid
- OrderService with @Transactional
- OrderRepository Spring Data interface
- Full test coverage with correct slicesRefactoring Legacy Code
Refactor UserController from field injection to constructor injection:
- Extract DTOs if using entities directly
- Add @Valid validation
- Add @RestControllerAdvice if missing
- Keep all existing functionalityWriting Tests
/spring-test UserService.java
Additional requirements:
- Mockito-based service unit tests using @ExtendWith(MockitoExtension.class)
- Mock all repository dependencies
- Test happy path + validation errors
- Test @Transactional rollback scenariosSpring Boot Best Practices
For project-type-specific best practices (e.g., JPA, MockMvc, WebTestClient, Mono/Flux, R2DBC, etc.), see the specialized best practices files linked above.
Project Structure & Architecture
- Use layered architecture: Controller, Service, Repository.
- Keep controllers thin; delegate logic to services.
- Use interfaces for service/repository layers for testability.
- Organize code by feature/module when possible.
Dependency Injection & Configuration
- Prefer constructor injection for mandatory dependencies.
- Use
@Valueor@ConfigurationPropertiesfor external config. - Avoid field injection except in tests.
- Use stereotypes:
@Component,@Service,@Repository,@Controller. - Keep config in
application.yml/application.propertiesand use profiles.
Data Access
- Use repository interfaces for CRUD/query methods.
- Prefer query methods or
@Queryover native queries. - Use DTOs for API responses, not entities.
Exception Handling
- Use global exception handling (e.g.,
@ControllerAdviceor@RestControllerAdvice). - Return meaningful error messages and HTTP status codes.
Testing
- Use integration tests for end-to-end scenarios.
- Use slice tests for controllers/services/repositories.
- Mock external dependencies.
- Use test profiles and in-memory databases for tests.
Security
- Use Spring Security for authentication/authorization.
- Store secrets in env variables or a secrets manager.
- Use method-level security for fine-grained access control.
Build & Dependency Management
- Use Maven/Gradle for dependencies.
- Keep dependencies up to date; avoid unnecessary libraries.
- Use dependency scopes appropriately.
REST API Design
- Use RESTful conventions for endpoints.
- Validate input with
@Validand constraint annotations. - Document APIs with Swagger/OpenAPI.
Logging & Monitoring
- Use SLF4J with Logback/Log4j2.
- Avoid logging sensitive data.
- Use Actuator for health checks/metrics.
Follow these patterns for this project:
Architecture
Controller (thin) → Service (@Transactional) → Repository (Spring Data)
Always use:
- Constructor injection
@ValidDTOs@RestControllerAdvice- Test slices (
@WebFluxTest,@DataR2dbcTest)
Commands:
./mvnw clean test # Unit + integration
./mvnw clean package # Build JAR
./mvnw spring-boot:run # Dev serverService Layer Pattern
@Service
@Transactional
class UserService {
private final UserRepository users;
private final UserMapper mapper;
public UserService(UserRepository users, UserMapper mapper) {
this.users = users;
this.mapper = mapper;
}
public Mono<UserResponse> create(CreateUserRequest request) {
return Mono.just(request)
.flatMap(this::validateRequest)
.map(mapper::toEntity)
.flatMap(users::save)
.map(mapper::toResponse);
}
private Mono<CreateUserRequest> validateRequest(CreateUserRequest request) {
if (request == null) {
return Mono.error(new IllegalArgumentException("CreateUserRequest must not be null"));
}
// Add field-level validation as needed, e.g.:
// if (request.getEmail() == null || request.getEmail().isBlank()) {
// return Mono.error(new IllegalArgumentException("Email must not be blank"));
// }
return Mono.just(request);
}
}Global Exception Handling
@RestControllerAdvice
class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleValidation(
MethodArgumentNotValidException ex) {
// Extract field errors, return 400 with details
}
@ExceptionHandler(EntityNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public ErrorResponse handleNotFound(EntityNotFoundException ex) {
return new ErrorResponse("resource.not.found", ex.getMessage());
}
}Test Slice Pattern
@WebFluxTest(UserController.class)
class UserControllerTest {
@Autowired
private WebTestClient webTestClient;
@MockBean
private UserService userService;
@Test
void createUser_validRequest_returns201() {
CreateUserRequest request = new CreateUserRequest("John", "john@example.com", "password123");
UserResponse response = new UserResponse("1", "John", "john@example.com");
when(userService.create(any())).thenReturn(Mono.just(response));
webTestClient.post().uri("/api/v1/users")
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(request)
.exchange()
.expectStatus().isCreated()
.expectBody()
.jsonPath("$.id").isEqualTo("1");
}
}Specialized Best Practices
- Use WebTestClient for controller testing.
- Prefer
@WebFluxTestfor controller slice tests. - Use
@RestControllerfor REST APIs. - Use
Mono<T>andFlux<T>for all controller/service/repository methods. - Avoid blocking calls; use only reactive APIs and repositories.
- Use Spring Data R2DBC or ReactiveMongoRepository for data access.
- Use
@ControllerAdvice/@RestControllerAdvicefor global exception handling. - Use
@Validfor request validation. - Use
@PreAuthorize/@Securedfor method-level security (with@EnableReactiveMethodSecurity). - Use
@RequestBodyand@ResponseBodyfor JSON APIs. - Use
@PathVariableand@RequestParamfor request data binding. - Use
@ExceptionHandlerfor controller-specific error handling. - Use
@ResponseStatusfor custom error responses. - Use
@CookieValuefor cookie data binding. - Use
@RequestHeaderfor header data binding. - Use
@SessionAttributesfor session-scoped model attributes (if needed). - Use
@CrossOriginfor CORS configuration.
Tips and Pitfalls
Do
- Keep CLAUDE.md current - update when adopting Spring AI, GraphQL, etc.
- Use skills for repetitive patterns - controllers/services/tests
- Provide concrete examples - 20-line controller > vague rules
- Specify Spring Boot version - 3.3 patterns differ from 2.x
- Test slices over full integration - faster, more focused tests
Avoid
- Constructor + field injection mix - pick one pattern
- Entities in REST responses - always use DTOs
- No global exception handling -
@RestControllerAdviceessential - Full
@SpringBootTesteverywhere - use slice tests first - Missing
@Transactional- services without transactions fail silently