Skip to content

Spring Boot: Best Practices for AI-Assisted Development

A practical guide for using Claude Code and GitHub Copilot effectively in Spring MVC projects, covering project setup, reusable skills, architecture patterns, and daily workflows.

Content Table

Purpose

Spring MVC 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 MVC-specific context for idiomatic code generation from the start.

By the end of this guide you will have:

  • An instruction file tailored to your Spring MVC project
  • Reusable skills for common Spring MVC tasks
  • Reference architecture snippets that AI assistants can follow
  • Practical workflows for daily Spring MVC development

Audience

  • Spring MVC developers using AI assistants for development, refactoring, or testing.
  • Teams standardizing AI usage across multiple Spring MVC codebases.

Prerequisites


Setting Up Your Spring MVC Project for AI

Configure three 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 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

IDE Instructions Files:

IDE/ToolFile
GitHub Copilot.github/copilot-instructions.md
Claude CodeCLAUDE.md
Cursorcursorrules.md

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

Spring MVC: Best Practices

Universal Best Practices

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 Patterns and Code Snippets

Controller (thin) -> Service (@Transactional) -> Repository (Spring Data)

Always use:

  • Constructor injection
  • @Valid DTOs
  • @RestControllerAdvice
  • Test slices (@WebMvcTest, @DataJpaTest)

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 UserResponse create(CreateUserRequest request) {
        User user = mapper.toEntity(request);
        user = users.save(user);
        return mapper.toResponse(user);
    }
}

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
@WebMvcTest(UserController.class)
class UserControllerTest {

    @Autowired
    private MockMvc mvc;

    @MockBean
    private UserService userService;

    @Test
    void createUser_validRequest_returns201() throws Exception {
        CreateUserRequest request = new CreateUserRequest("John", "john@example.com", "password123");
        UserResponse response = new UserResponse("1", "John", "john@example.com");

        when(userService.create(request)).thenReturn(response);

        mvc.perform(post("/api/v1/users")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(request)))
            .andExpect(status().isCreated())
            .andExpect(jsonPath("$.id").value("1"));
    }
}

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:
- @ServiceTest slice test
- Mock all repository dependencies
- Test happy path + validation errors
- Test @Transactional rollback scenarios

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

Spring MVC Specialized Best Practices

  • Use MockMvc for controller testing.
  • Prefer @WebMvcTest for controller slice tests.
  • Use @ControllerAdvice for global exception handling.
  • Use @RestController for REST APIs, @Controller for MVC views.
  • Use Spring Data JPA for data access.
  • Use @Transactional for service/repository methods that modify data.
  • Use @Valid for request validation.
  • Use @PreAuthorize/@Secured for method-level security.
  • Use @ModelAttribute for form binding in MVC views.
  • Use Thymeleaf or JSP for server-side rendering if needed.
  • Use @InitBinder for custom binding/validation logic.
  • Use @ResponseStatus for custom error responses.
  • Use @ExceptionHandler for controller-specific error handling.
  • Use @PathVariable and @RequestParam for request data binding.
  • Use @SessionAttributes for session-scoped model attributes.
  • Use @CookieValue for cookie data binding.
  • Use @RequestBody and @ResponseBody for JSON/XML APIs.

References

Copyright © MSG Systems Romania AI Labs