Protocol for adding functionality to existing Spring Boot code while maintaining backward compatibility.
npx claudepluginhub sumanpapanaboina1983/adlc-accelerator-kit-pluginsThis skill uses the workspace's default tool permissions.
| Gate | Threshold | Status |
Provides Spring Boot best practices for project setup, dependency injection, configuration, REST controllers with DTOs/validation, services/transactions, JPA repositories, and logging.
Provides Spring Boot mastery covering auto-configuration, security, Data JPA, Actuator, and TestContainers testing for Java/Kotlin backends.
Guides test-driven development for Spring Boot using JUnit 5, Mockito, MockMvc, Testcontainers, and JaCoCo. Use when adding features, fixing bugs, or refactoring.
Share bugs, ideas, or general feedback.
| Gate | Threshold | Status |
|---|---|---|
| Compile | 0 errors | REQUIRED |
| Tests | 100% pass (0 failures) | REQUIRED |
| Coverage | ≥ 80% line coverage | REQUIRED |
| Checkstyle | 0 violations | REQUIRED (if configured) |
⛔ BLOCKING: Enhancement is NOT complete until ALL gates pass.
Before ANY enhancement, verify JaCoCo is configured:
grep -l "jacoco-maven-plugin" pom.xml
If NOT configured, add JaCoCo plugin to pom.xml first (see springboot-testing skill for full configuration).
When adding to EXISTING code (not building from scratch):
┌─────────────────────────────────────────────────────────────┐
│ STEP 0: VERIFY COVERAGE CONFIGURATION │
│ - Check: grep "jacoco-maven-plugin" pom.xml │
│ - If missing → Configure JaCoCo FIRST │
│ - ⛔ Do NOT proceed without coverage configuration │
│ │
│ STEP 1: READ EXISTING CODE FIRST │
│ - Read the existing controller/service/repository │
│ - Understand current patterns, naming, structure │
│ - Note: endpoint paths, method names, error handling style │
│ │
│ STEP 2: IDENTIFY PATTERNS TO FOLLOW │
│ - How are existing endpoints structured? │
│ - What naming convention is used? │
│ - How is error handling done? │
│ - How are tests organized? │
│ │
│ STEP 3: WRITE NEW TESTS ONLY │
│ - Add NEW test methods — don't modify existing tests │
│ - Follow existing test naming patterns │
│ - Cover: happy path, error cases, edge cases │
│ │
│ STEP 4: IMPLEMENT FOLLOWING EXISTING PATTERNS │
│ - Match existing code style exactly │
│ - Use same error handling approach │
│ - Follow same layer conventions │
│ │
│ STEP 5: VERIFY NO REGRESSIONS │
│ - Run FULL test suite (existing + new) │
│ - All existing tests must still pass │
│ - No changes to existing behavior │
└─────────────────────────────────────────────────────────────┘
Before adding ANY code, read and note:
# Read the existing controller
Read src/main/java/.../TodoController.java
# Note: @RequestMapping base path, method patterns, response types
# Read the existing service
Read src/main/java/.../TodoService.java
# Note: Method naming, transaction usage, exception handling
# Read the existing repository
Read src/main/java/.../TodoRepository.java
# Note: Query patterns, return types
# Read existing tests
Read src/test/java/.../TodoControllerTest.java
# Note: Test naming, setup patterns, assertion style
# Read existing DTOs
Read src/main/java/.../dto/
# Note: Which DTOs exist, can you reuse them?
| Aspect | Question to Answer |
|---|---|
| Base Path | /api/v1/todos or /todos or something else? |
| ID in Path | /{id} or /{todoId} or query param? |
| Response Wrapper | Raw entity, ResponseEntity, custom wrapper? |
| Error Format | ErrorResponse DTO? Map? String? |
| Naming | findAll() or getAll() or list()? |
| Test Naming | should_X_whenY() or testX() or xTest()? |
| Mock Style | @MockBean or @Mock + @InjectMocks? |
// EXISTING TodoController.java
@RestController
@RequestMapping("/api/v1/todos")
@RequiredArgsConstructor
public class TodoController {
private final TodoService todoService;
@GetMapping
public ResponseEntity<List<TodoResponse>> getAll() {
return ResponseEntity.ok(todoService.findAll());
}
@PostMapping
public ResponseEntity<TodoResponse> create(
@Valid @RequestBody CreateTodoRequest request) {
return ResponseEntity.status(HttpStatus.CREATED)
.body(todoService.create(request));
}
@GetMapping("/{id}")
public ResponseEntity<TodoResponse> getById(@PathVariable Long id) {
return ResponseEntity.ok(todoService.findById(id));
}
}
/api/v1/todos/{id} as @PathVariable LongResponseEntity<TodoResponse>findAll(), create(), findById()// ADD to existing TodoControllerTest.java - DO NOT MODIFY existing tests
@Test
void should_return204_whenDeleteExistingTodo() {
// Given
doNothing().when(todoService).delete(1L);
// When & Then
mockMvc.perform(delete("/api/v1/todos/1"))
.andExpect(status().isNoContent());
verify(todoService).delete(1L);
}
@Test
void should_return404_whenDeleteNonExistentTodo() {
// Given
doThrow(new TodoNotFoundException(999L)).when(todoService).delete(999L);
// When & Then
mockMvc.perform(delete("/api/v1/todos/999"))
.andExpect(status().isNotFound());
}
// ADD to existing TodoController.java - follow existing style
@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable Long id) {
todoService.delete(id);
return ResponseEntity.noContent().build();
}
// ADD to existing TodoService.java - follow existing style
public void delete(Long id) {
if (!todoRepository.existsById(id)) {
throw new TodoNotFoundException(id);
}
todoRepository.deleteById(id);
}
mvn test # ALL tests: existing + new must pass
// EXISTING
public record TodoResponse(Long id, String title, boolean completed) {}
// ENHANCED - ADD fields only
public record TodoResponse(
Long id,
String title,
boolean completed,
Instant updatedAt // NEW - backward compatible (additive)
) {}
// EXISTING
@GetMapping
public ResponseEntity<List<TodoResponse>> getAll() { ... }
// ENHANCED - optional params
@GetMapping
public ResponseEntity<List<TodoResponse>> getAll(
@RequestParam(required = false) Boolean completed) { ... }
// BREAKING: Removing field
// Before: {"id": 1, "title": "...", "priority": 5}
// After: {"id": 1, "title": "..."} // priority removed!
// BREAKING: Renaming field
// Before: {"todoId": 1}
// After: {"id": 1} // field renamed!
// BREAKING: Changing type
// Before: {"id": "abc-123"} // String
// After: {"id": 123} // Long
// BREAKING: Changing endpoint path
// Before: GET /api/v1/todos
// After: GET /api/v2/tasks
// DON'T rename existing methods
// Before: findAll() → After: getAllTodos() ❌
// DON'T change existing method signatures
// Before: findById(Long id)
// After: findById(Long id, boolean includeDeleted) ❌
// DON'T modify existing tests
// If existing test expects X and you need Y, that's a
// breaking change, not an enhancement ❌
// DON'T remove existing fields from responses
// Before: {"id": 1, "title": "...", "priority": 5}
// After: {"id": 1, "title": "..."} ❌
// DON'T change existing error response shapes
// Clients may depend on the current format ❌
Before declaring done:
Before marking enhancement complete:
# Generate coverage report
mvn test jacoco:report
# Check coverage
open target/site/jacoco/index.html
| Metric | Minimum Threshold |
|---|---|
| Line Coverage | 80% |
| Branch Coverage | 75% |
If coverage < 80%:
mvn test jacoco:report⛔ Do NOT mark complete if coverage < 80%
A pre-completion hook enforces this requirement automatically.