home / skills / physics91 / claude-vibe / kotlin-spring-reviewer
This skill reviews Spring Kotlin projects for idioms, coroutine integration, WebFlux patterns, and data class practices to improve reliability.
npx playbooks add skill physics91/claude-vibe --skill kotlin-spring-reviewerReview the files below or copy the command above to add this skill to your agents.
---
name: kotlin-spring-reviewer
description: |
WHEN: Spring Boot + Kotlin, Ktor backend review, coroutine-based server, WebFlux/R2DBC pattern checks
WHAT: Spring Kotlin idioms + Coroutines integration + WebFlux patterns + Data class usage + Test strategies
WHEN NOT: Android → kotlin-android-reviewer, KMP shared code → kotlin-multiplatform-reviewer
---
# 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 `spring-boot` or `ktor` in `build.gradle.kts`
## Project Detection
- `org.springframework.boot` plugin in `build.gradle.kts`
- `io.ktor` dependency in `build.gradle.kts`
- `application.yml` or `application.properties`
- `Application.kt` main class
## 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 |
```kotlin
// 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 |
```kotlin
// 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:**
```kotlin
// 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 |
```kotlin
// 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 |
```kotlin
// 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 |
```kotlin
// 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 |
```kotlin
// 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
1. **Constructor Injection**: Use default constructor instead of @Autowired
2. **Immutability**: val, data class (except Entity)
3. **Coroutines**: suspend functions, structured concurrency
4. **Kotlin DSL**: coRouter, bean { }
5. **Testing**: MockK, Kotest, @SpringBootTest
## Integration
- `code-reviewer` skill: General code quality
- `kotlin-multiplatform-reviewer` skill: KMP server sharing
- `security-scanner` skill: API security checks
## Notes
- Based on Spring Boot 3.x + Kotlin 1.9+
- WebFlux/R2DBC reactive stack support
- Ktor 2.x support
This skill reviews Spring Boot + Kotlin and Ktor backend projects to surface Kotlin idiom issues, coroutine integration problems, WebFlux/R2DBC anti-patterns, and data class design pitfalls. It highlights high-severity problems (e.g., runBlocking, GlobalScope, field injection, data class entities) and gives concrete remediation steps. The goal is practical, actionable feedback to improve correctness, performance, and maintainability of coroutine-based servers.
The reviewer scans build files, main application entries, and Kotlin source to detect Spring Boot or Ktor projects and the reactive stack (WebFlux, R2DBC). It runs rule checks for Kotlin idioms, Spring-specific patterns (injection, all-open/jpa plugins), coroutine usage, WebFlux-to-coroutines conversions, Ktor routing/DI, and DTO/entity design. Results are categorized by severity and include suggested fixes like converting runBlocking to suspend, replacing GlobalScope with structured application scope, and converting field injection to constructor injection.
Does the reviewer support both WebFlux and Ktor?
Yes. It checks Spring WebFlux + R2DBC reactive stacks and Ktor 2.x server patterns, applying relevant coroutine and routing rules for each framework.
Will it change code automatically?
No. The skill produces a prioritized checklist and concrete recommendations; automated fixes are left to the developer or separate tooling.