Claude-skill-registry kotlin-spring-reviewer
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/kotlin-spring-reviewer" ~/.claude/skills/majiayu000-claude-skill-registry-kotlin-spring-reviewer && rm -rf "$T"
manifest:
skills/data/kotlin-spring-reviewer/SKILL.mdsource content
Kotlin Spring Reviewer Skill
Purpose
Reviews Spring Boot + Kotlin and Ktor backend projects for Kotlin idioms, Coroutines integration, WebFlux, and data class best practices.
When to Use
- Spring Boot + Kotlin project code review
- Ktor server project review
- "WebFlux", "R2DBC", "Coroutines server" mentions
- Projects with
orspring-boot
inktorbuild.gradle.kts
Project Detection
plugin inorg.springframework.bootbuild.gradle.kts
dependency inio.ktorbuild.gradle.kts
orapplication.ymlapplication.properties
main classApplication.kt
Workflow
Step 1: Analyze Project
**Framework**: Spring Boot 3.2.x **Kotlin**: 1.9.x **Build Tool**: Gradle (Kotlin DSL) **Dependencies**: - Spring WebFlux (reactive) - Spring Data R2DBC - Kotlinx Coroutines
Step 2: Select Review Areas
AskUserQuestion:
"Which areas to review?" Options: - Full Kotlin Spring pattern check (recommended) - Kotlin idiom usage - Coroutines/WebFlux integration - Data class/DTO design - Test strategies multiSelect: true
Detection Rules
Kotlin Idioms
| Check | Recommendation | Severity |
|---|---|---|
| Java-style getter/setter | Use Kotlin property | LOW |
| if-based null check | Use ?.let, ?:, avoid !! | MEDIUM |
| if-else chain | Use when expression | LOW |
| Missing extension functions | Utility → extension function | LOW |
| Missing scope functions | Use apply, let, run, also | LOW |
// BAD: Java style class User { private var name: String? = null fun getName(): String? = name fun setName(name: String?) { this.name = name } } // GOOD: Kotlin property class User { var name: String? = null } // BAD: Java-style null check fun process(user: User?) { if (user != null) { if (user.name != null) { println(user.name) } } } // GOOD: Kotlin null-safe operators fun process(user: User?) { user?.name?.let { println(it) } } // BAD: if-else chain fun getStatus(code: Int): String { if (code == 200) return "OK" else if (code == 404) return "Not Found" else return "Unknown" } // GOOD: when expression fun getStatus(code: Int): String = when (code) { 200 -> "OK" 404 -> "Not Found" else -> "Unknown" }
Spring + Kotlin Patterns
| Check | Recommendation | Severity |
|---|---|---|
| @Autowired field injection | Constructor injection | HIGH |
| lateinit var abuse | Constructor injection or lazy | MEDIUM |
| Missing open class | Use all-open plugin | HIGH |
| data class @Entity | Use regular class | HIGH |
// BAD: Field injection @Service class UserService { @Autowired private lateinit var userRepository: UserRepository } // GOOD: Constructor injection (Kotlin default) @Service class UserService( private val userRepository: UserRepository ) // BAD: data class as JPA Entity @Entity data class User( @Id val id: Long, val name: String ) // equals/hashCode issues // GOOD: Regular class with explicit equals/hashCode @Entity class User( @Id @GeneratedValue(strategy = GenerationType.IDENTITY) var id: Long? = null, var name: String ) { override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is User) return false return id != null && id == other.id } override fun hashCode(): Int = javaClass.hashCode() }
Gradle Plugin Check:
// build.gradle.kts plugins { kotlin("plugin.spring") // all-open for Spring kotlin("plugin.jpa") // no-arg for JPA entities }
Coroutines Integration
| Check | Recommendation | Severity |
|---|---|---|
| runBlocking in controller | Use suspend function | CRITICAL |
| GlobalScope in server | Use structured concurrency | CRITICAL |
| Missing Dispatcher | Specify IO/Default | MEDIUM |
| Missing exception handling | Use CoroutineExceptionHandler | HIGH |
// BAD: runBlocking in controller @GetMapping("/users") fun getUsers(): List<User> = runBlocking { userService.getUsers() } // GOOD: suspend function (WebFlux/Coroutines) @GetMapping("/users") suspend fun getUsers(): List<User> = userService.getUsers() // BAD: GlobalScope in service @Service class UserService { fun processAsync() { GlobalScope.launch { // Dangerous: Not cancelled on app shutdown } } } // GOOD: Structured concurrency @Service class UserService( private val applicationScope: CoroutineScope ) { fun processAsync() = applicationScope.launch { // Properly cancelled on app shutdown } }
WebFlux + Coroutines
| Check | Recommendation | Severity |
|---|---|---|
| Direct Mono/Flux usage | Convert to suspend/Flow | MEDIUM |
| awaitSingle abuse | Use coRouter DSL | LOW |
| Blocking call | Use Dispatchers.IO | CRITICAL |
// OK: Direct Mono/Flux @GetMapping("/user/{id}") fun getUser(@PathVariable id: Long): Mono<User> = userRepository.findById(id) // BETTER: Kotlin Coroutines @GetMapping("/user/{id}") suspend fun getUser(@PathVariable id: Long): User? = userRepository.findById(id).awaitSingleOrNull() // BEST: coRouter DSL (functional endpoints) @Configuration class RouterConfig { @Bean fun routes(handler: UserHandler) = coRouter { "/api/users".nest { GET("", handler::getAll) GET("/{id}", handler::getById) POST("", handler::create) } } } class UserHandler(private val service: UserService) { suspend fun getAll(request: ServerRequest): ServerResponse = ServerResponse.ok().bodyAndAwait(service.getAll()) }
Ktor Patterns
| Check | Recommendation | Severity |
|---|---|---|
| Excessive routing nesting | Split into modules | MEDIUM |
| No DI | Use Koin/Kodein | MEDIUM |
| Missing error handling | Use StatusPages plugin | HIGH |
| Missing serialization | Use ContentNegotiation | HIGH |
// BAD: All routes in one file fun Application.module() { routing { get("/users") { /* ... */ } get("/users/{id}") { /* ... */ } get("/products") { /* ... */ } // ... 100 more } } // GOOD: Split into modules fun Application.module() { configureRouting() configureSerialization() configureDI() } fun Application.configureRouting() { routing { userRoutes() productRoutes() } } fun Route.userRoutes() { route("/users") { get { /* ... */ } get("/{id}") { /* ... */ } post { /* ... */ } } }
Data Class Design
| Check | Recommendation | Severity |
|---|---|---|
| var in DTO | Use val (immutable) | MEDIUM |
| Excessive nullable | Use defaults or required | LOW |
| Missing validation | Use @field:Valid, init {} | MEDIUM |
// BAD: Mutable DTO data class CreateUserRequest( var name: String?, var email: String? ) // GOOD: Immutable + validation data class CreateUserRequest( @field:NotBlank val name: String, @field:Email val email: String ) { init { require(name.length <= 100) { "Name too long" } } }
Response Template
## Kotlin Spring Code Review Results **Project**: [name] **Spring Boot**: 3.2.x | **Kotlin**: 1.9.x **Stack**: WebFlux + R2DBC + Coroutines ### Kotlin Idioms | Status | File | Issue | |--------|------|-------| | LOW | UserService.kt | Java-style null check → ?.let recommended | ### Spring Patterns | Status | File | Issue | |--------|------|-------| | HIGH | ProductService.kt | @Autowired field injection → constructor injection | | HIGH | User.kt | data class @Entity → regular class | ### Coroutines | Status | File | Issue | |--------|------|-------| | CRITICAL | ReportService.kt | runBlocking in controller | | HIGH | BatchJob.kt | GlobalScope usage | ### Recommended Actions 1. [ ] Verify kotlin-spring, kotlin-jpa plugins 2. [ ] runBlocking → suspend function conversion 3. [ ] GlobalScope → applicationScope injection 4. [ ] data class Entity → regular class change
Best Practices
- Constructor Injection: Use default constructor instead of @Autowired
- Immutability: val, data class (except Entity)
- Coroutines: suspend functions, structured concurrency
- Kotlin DSL: coRouter, bean { }
- Testing: MockK, Kotest, @SpringBootTest
Integration
skill: General code qualitycode-reviewer
skill: KMP server sharingkotlin-multiplatform-reviewer
skill: API security checkssecurity-scanner
Notes
- Based on Spring Boot 3.x + Kotlin 1.9+
- WebFlux/R2DBC reactive stack support
- Ktor 2.x support