Skip to content

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

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


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 AssistantSkills LocationFile FormatDocs
Claude Code.claude/skills/<name>/SKILL.mdMarkdown with YAML frontmatterClaude Code Skills
Cursor.cursor/skills/<name>/SKILL.mdMarkdown with YAML frontmatterCursor Skills
GitHub Copilot.github/skills/<name>.md or
.claude/skills/<name>/SKILL.md
Markdown with YAML frontmatterCopilot 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

ResourceURLUsage
Spring AI Referencedocs.spring.io/spring-aiAI integration patterns
Spring Boot AI Guidespring.io/blog/spring-aiOfficial AI development guidance
Spring REST Docsspring.io/projects/spring-restdocsAPI documentation patterns
SpringDoc OpenAPIspringdoc.orgAuto-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 AssistantContext FileLocation
Claude CodeCLAUDE.mdProject root or .claude/
GitHub Copilotcopilot-instructions.md.github/
Cursor.cursorrulesProject 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.java

Prompt 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 slices

Refactoring 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 functionality

Writing 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 scenarios

Spring 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 @Value or @ConfigurationProperties for external config.
  • Avoid field injection except in tests.
  • Use stereotypes: @Component, @Service, @Repository, @Controller.
  • Keep config in application.yml/application.properties and use profiles.

Data Access

  • Use repository interfaces for CRUD/query methods.
  • Prefer query methods or @Query over native queries.
  • Use DTOs for API responses, not entities.

Exception Handling

  • Use global exception handling (e.g., @ControllerAdvice or @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 @Valid and 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
  • @Valid DTOs
  • @RestControllerAdvice
  • Test slices (@WebFluxTest, @DataR2dbcTest)

Commands:

bash
./mvnw clean test    # Unit + integration
./mvnw clean package # Build JAR
./mvnw spring-boot:run  # Dev server

Service Layer Pattern

java
@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

java
@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

java
@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 @WebFluxTest for controller slice tests.
  • Use @RestController for REST APIs.
  • Use Mono<T> and Flux<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/@RestControllerAdvice for global exception handling.
  • Use @Valid for request validation.
  • Use @PreAuthorize/@Secured for method-level security (with @EnableReactiveMethodSecurity).
  • Use @RequestBody and @ResponseBody for JSON APIs.
  • Use @PathVariable and @RequestParam for request data binding.
  • Use @ExceptionHandler for controller-specific error handling.
  • Use @ResponseStatus for custom error responses.
  • Use @CookieValue for cookie data binding.
  • Use @RequestHeader for header data binding.
  • Use @SessionAttributes for session-scoped model attributes (if needed).
  • Use @CrossOrigin for 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 - @RestControllerAdvice essential
  • Full @SpringBootTest everywhere - use slice tests first
  • Missing @Transactional - services without transactions fail silently

References

Copyright © MSG Systems Romania AI Labs