Skip to content

Commit 6e65150

Browse files
committed
docs: reorganize agent instruction files
1 parent bc33abb commit 6e65150

2 files changed

Lines changed: 360 additions & 419 deletions

File tree

.github/copilot-instructions.md

Lines changed: 183 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,112 +1,213 @@
1-
# GitHub Copilot Instructions
1+
# Copilot Instructions
22

3-
> **Token Efficiency Note**: This is a minimal pointer file (~500 tokens, auto-loaded by Copilot).
4-
> For complete operational details, reference: `#file:AGENTS.md` (~2,500 tokens, loaded on-demand)
5-
> For specialized knowledge, use: `#file:SKILLS/<skill-name>/SKILL.md` (loaded on-demand when needed)
3+
## Project Overview
64

7-
## Quick Context
5+
This is a Spring Boot REST API for managing football players, demonstrating modern Java patterns and clean architecture. It's a learning-focused proof-of-concept that follows Spring Boot best practices with layered architecture (Controller → Service → Repository).
86

9-
**Project**: Spring Boot REST API demonstrating modern Java patterns
10-
**Stack**: Java 25 (LTS) • Spring Boot 4 • JPA/Hibernate • SQLite • Maven • Docker
11-
**Pattern**: Controller → Service → Repository → JPA (layered architecture)
12-
**Philosophy**: Learning-focused PoC emphasizing Spring Boot best practices
7+
## Stack
138

14-
## Core Conventions
9+
- **Java**: 25 (LTS, required for consistency)
10+
- **Spring Boot**: 4.0.0 (with Spring MVC)
11+
- **ORM**: Spring Data JPA with Hibernate
12+
- **Database**: SQLite (file-based for runtime, in-memory for tests)
13+
- **Build Tool**: Maven 3 (use `./mvnw` wrapper, NOT system Maven)
14+
- **Containers**: Docker with Docker Compose
15+
- **Logging**: SLF4J with Logback
16+
- **Monitoring**: Spring Boot Actuator
17+
- **API Docs**: SpringDoc OpenAPI 3 (Swagger UI)
1518

16-
- **Naming**: camelCase (methods/variables), PascalCase (classes)
17-
- **Annotations**: Use Spring stereotypes (@RestController, @Service, @Repository)
18-
- **Lombok**: Reduce boilerplate (@Data, @Builder, @AllArgsConstructor)
19-
- **Dependency Injection**: Constructor injection (Lombok @RequiredArgsConstructor)
20-
- **Testing**: JUnit 5 + AssertJ for fluent assertions
21-
- **Build**: Use `./mvnw` wrapper, NOT system Maven
22-
- **Commit Messages**: Follow Conventional Commits with issue number suffix
23-
- Format: `type(scope): description (#issue)` (max 80 chars)
24-
- Example: `docs: optimize AI agent instructions for token efficiency (#259)`
25-
- Types: `feat`, `fix`, `chore`, `docs`, `test`, `refactor`
19+
## Project Patterns
2620

27-
## Test Naming Convention
21+
- **Layered architecture**: Controller → Service → Repository → Database
22+
- **Dependency injection**: Constructor injection via Lombok's @RequiredArgsConstructor
23+
- **DTO pattern**: Never expose entities in controllers, use DTOs with ModelMapper
24+
- **Caching**: Spring Cache abstraction (@Cacheable) with 1-hour TTL
25+
- **Error handling**: @ControllerAdvice for global exception handling
26+
- **Validation**: Bean Validation (JSR-380) in DTOs
27+
- **Repository queries**: Mix of derived queries (findBySquadNumber) and custom JPQL (@Query)
28+
- **Date handling**: ISO-8601 strings with custom JPA converter for SQLite compatibility
2829

29-
**Pattern**: `method_scenario_outcome`
30+
## Code Conventions
3031

31-
- **method**: The method being tested (e.g., `post`, `findById`, `create`)
32-
- **scenario**: The context or condition (e.g., `playerExists`, `invalidData`, `noMatches`)
33-
- **outcome**: The expected result (e.g., `returnsPlayer`, `returnsConflict`, `returnsEmpty`)
32+
### Naming
33+
34+
- **Classes**: PascalCase (e.g., `PlayersController`, `PlayerDTO`)
35+
- **Methods/Variables**: camelCase (e.g., `findById`, `squadNumber`)
36+
- **Test methods**: `method_scenario_outcome` (e.g., `post_squadNumberExists_returnsConflict`)
37+
38+
### Annotations
39+
40+
- **Controllers**: @RestController (never @Controller for REST APIs)
41+
- **DTOs**: Lombok @Data, @Builder, @AllArgsConstructor
42+
- **Services**: @Service + @Transactional on mutating methods
43+
- **Repositories**: @Repository (extend JpaRepository)
44+
- **Entities**: @Entity with @Table, @Id, @GeneratedValue
45+
46+
### Lombok usage
47+
48+
- Reduce boilerplate with @Data, @Builder, @AllArgsConstructor
49+
- Use @RequiredArgsConstructor for constructor injection
50+
- Never use field injection
51+
52+
### REST conventions
53+
54+
- Return ResponseEntity<T> from controllers
55+
- Use proper HTTP status codes (201 Created, 204 No Content, 409 Conflict)
56+
- OpenAPI annotations required on all endpoints
57+
58+
### Logging
59+
60+
- Use SLF4J (injected via Lombok @Slf4j)
61+
- Never use System.out.println
62+
63+
### Configuration
64+
65+
- Externalize via @Value or application.yml
66+
- Never hardcode values in code
67+
68+
## Testing
69+
70+
### Framework
71+
72+
- **Unit tests**: JUnit 5 with AssertJ for fluent assertions
73+
- **Integration tests**: @SpringBootTest with MockMvc
74+
- **Coverage**: JaCoCo reports (must maintain high coverage)
75+
76+
### Test naming
77+
78+
**Pattern**: `givenX_whenY_thenZ()` (BDD Given-When-Then)
79+
80+
**Naming philosophy:**
81+
82+
Use **semantic naming** that describes the business/domain state and behavior, not technical implementation details:
83+
84+
- **Given** (precondition): Describe the state of the system or data
85+
- `givenPlayerExists` - A player is present in the system
86+
- `givenNoExistingPlayer` - No player matching criteria
87+
- `givenInvalidPlayer` - Invalid data provided
88+
- `givenRaceCondition` - Concurrent modification scenario
89+
- `givenNullId` - Missing required identifier
90+
- **When** (action): The method/operation being tested
91+
- `whenCreate` - Creating a new entity
92+
- `whenRetrieveById` - Fetching by identifier
93+
- `whenUpdate` - Updating existing entity
94+
- `whenDelete` - Removing an entity
95+
- `whenSearchByLeague` - Searching/filtering operation
96+
- **Then** (outcome): Expected result or behavior
97+
- `thenReturnsPlayerDTO` - Returns the DTO object
98+
- `thenReturnsNull` - Returns null (not found/conflict)
99+
- `thenReturnsTrue` / `thenReturnsFalse` - Boolean success indicator
100+
- `thenReturns26Players` - Specific count of results
101+
- `thenReturnsEmptyList` - No results found
102+
- `thenReturnsOk` - HTTP 200 response
103+
- `thenReturnsNotFound` - HTTP 404 response
104+
- `thenReturnsCreated` - HTTP 201 response
105+
106+
**Code conventions:**
107+
108+
- Comments: Use **Given/When/Then** (BDD pattern)
109+
- Assertions: Use `then()` from AssertJ BDDAssertions
110+
- Variables:
111+
- Use `actual` for operation results
112+
- Use `expected` for comparison values when verifying equality
113+
- Use `existing` for pre-saved entities in database
114+
115+
**Why BDD Given-When-Then?**
116+
117+
-**Readable**: Natural language flow, accessible to all stakeholders
118+
-**Behavior-focused**: Tests business logic, not implementation details
119+
-**Self-documenting**: Method name clearly states test scenario
120+
-**Framework-aligned**: Matches Cucumber/SpecFlow patterns
34121

35-
**Examples**:
36122
```java
37-
// Controller: post_squadNumberExists_returnsConflict()
38-
// Service: create_noConflict_returnsPlayerDTO()
39-
// Repository: findById_playerExists_returnsPlayer()
123+
// Examples across layers:
124+
givenNoExistingPlayer_whenCreate_thenReturnsPlayerDTO() // Service: success case
125+
givenPlayerAlreadyExists_whenCreate_thenReturnsNull() // Service: conflict case
126+
givenPlayerExists_whenFindById_thenReturnsPlayer() // Repository: found
127+
givenPlayerDoesNotExist_whenFindById_thenReturnsEmpty() // Repository: not found
128+
givenValidPlayer_whenPost_thenReturnsCreated() // Controller: HTTP 201
129+
givenPlayerDoesNotExist_whenGetById_thenReturnsNotFound() // Controller: HTTP 404
40130
```
41131

42-
**JavaDoc**: Use proper BDD (Given/When/Then) structure in comments:
132+
### Test structure
133+
134+
Use BDD (Given/When/Then) consistently in JavaDoc comments, method names, and code sections:
135+
43136
```java
44137
/**
45-
* Given a player with squad number 5 already exists in the database
46-
* When POST /players is called with a new player using squad number 5
47-
* Then response status is 409 Conflict
138+
* Given no existing player with the same squad number
139+
* When create() is called with valid player data
140+
* Then the player is saved and a PlayerDTO is returned
48141
*/
49142
@Test
50-
void post_squadNumberExists_returnsConflict() { ... }
143+
void givenNoExistingPlayer_whenCreate_thenReturnsPlayerDTO() {
144+
// Given
145+
Player player = PlayerFakes.createOneValid();
146+
PlayerDTO expected = PlayerDTOFakes.createOneValid();
147+
// When
148+
PlayerDTO actual = playersService.create(expected);
149+
// Then
150+
then(actual).isEqualTo(expected);
151+
}
51152
```
52153

53-
**Benefits**: Concise method names for IDE test runners, full BDD context in JavaDoc for code readability.
54-
55-
## Architecture at a Glance
56-
154+
### Test requirements
155+
156+
- Unit tests required for all service and repository methods
157+
- Integration tests required for all controller endpoints
158+
- Tests use in-memory SQLite (jdbc:sqlite::memory:)
159+
- All tests must pass before PR (`./mvnw clean install`)
160+
- **Assertion quality**: Verify actual behavior, not just counts (e.g., verify league content, not just `hasSize()`)
161+
162+
## Avoid
163+
164+
- **Field injection** - Always use constructor injection
165+
- **Using `new` for Spring beans** - Breaks dependency injection
166+
- **Missing @Transactional** - Required on service methods that modify data
167+
- **Exposing entities in controllers** - Always use DTOs
168+
- **System.out.println** - Use SLF4J logging
169+
- **Hardcoded config** - Use @Value or application.yml
170+
- **System Maven** - Always use `./mvnw` wrapper for consistency
171+
- **SQLite in production** - This is a demo/development-only setup
172+
173+
## Folder Structure
174+
175+
```tree
176+
src/main/java/ # Application code
177+
├── Application.java # @SpringBootApplication entry point
178+
├── controllers/ # REST endpoints
179+
├── services/ # Business logic + caching
180+
├── repositories/ # Data access (Spring Data JPA)
181+
├── models/ # Entities (Player) and DTOs (PlayerDTO)
182+
└── converters/ # JPA converters (ISO date handling)
183+
src/test/java/ # Test classes (mirrors main structure)
184+
storage/ # SQLite database file (runtime)
185+
scripts/ # Docker entrypoint and healthcheck
57186
```
58-
Controller → Service → Repository → JPA → Database
59-
↓ ↓ ↓
60-
Validation Cache Query Methods
61-
```
62-
63-
- **Controllers**: REST endpoints with @RestController
64-
- **Services**: Business logic with @Service + caching
65-
- **Repositories**: JpaRepository with derived queries
66-
- **DTOs**: ModelMapper for entity ↔ DTO transformations
67-
- **Cache**: Spring Cache abstraction (1-hour TTL)
68-
69-
## Copilot Should
70-
71-
- Generate idiomatic Spring Boot code with proper annotations
72-
- Use JPA repository patterns (derived queries, @Query)
73-
- Follow REST conventions with ResponseEntity<T>
74-
- Write tests with @SpringBootTest and MockMvc
75-
- Apply Lombok annotations to reduce boilerplate
76-
- Use ModelMapper for DTO transformations
77-
- Implement proper exception handling with @ControllerAdvice
78187

79-
## Copilot Should Avoid
80-
81-
- Field injection (use constructor injection)
82-
- Using `new` for services (breaks DI)
83-
- Missing @Transactional on service methods
84-
- Exposing entities directly in controllers (use DTOs)
85-
- System.out.println (use SLF4J logging)
86-
- Hardcoded configuration (use @Value or application.yml)
87-
88-
## Quick Commands
188+
## Quick Reference
89189

90190
```bash
91-
# Run with hot reload
92-
./mvnw spring-boot:run
93-
94-
# Test with coverage
95-
./mvnw clean test jacoco:report
191+
# Development
192+
./mvnw spring-boot:run # Run with hot reload
193+
./mvnw clean test jacoco:report # Test with coverage
96194

97195
# Docker
98-
docker compose up
196+
docker compose up # Start in container
99197

100-
# Swagger: http://localhost:9000/swagger-ui/index.html
101-
# Actuator: http://localhost:9001/actuator/health
198+
# Documentation
199+
http://localhost:8080/swagger-ui.html # API docs
200+
http://localhost:8080/actuator/health # Health check
102201
```
103202

104-
## Need More Detail?
203+
## Commit Messages
204+
205+
Follow Conventional Commits with issue number suffix:
105206

106-
**For operational procedures**: Load `#file:AGENTS.md`
107-
**For Docker expertise**: *(Planned)* `#file:SKILLS/docker-containerization/SKILL.md`
108-
**For testing patterns**: *(Planned)* `#file:SKILLS/testing-patterns/SKILL.md`
207+
- **Format**: `type(scope): description (#issue)` (max 80 chars)
208+
- **Types**: feat, fix, chore, docs, test, refactor
209+
- **Example**: `feat(api): add squad number search endpoint (#123)`
109210

110-
---
211+
## Additional Context
111212

112-
**Why this structure?** Copilot auto-loads this file on every chat (~500 tokens). Loading `AGENTS.md` or `SKILLS/` explicitly gives you deep context only when needed, saving 80% of your token budget!
213+
For detailed operational procedures, workflows, and troubleshooting, see `AGENTS.md`.

0 commit comments

Comments
 (0)