xiaohongshu
xiaohongshu
kotlin-spring-reviewer
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-rev...
Full skill instructions
Reviews Spring Boot + Kotlin and Ktor backend projects for Kotlin idioms, Coroutines integration, WebFlux, and data class best practices.
spring-boot or ktor in build.gradle.ktsorg.springframework.boot plugin in build.gradle.ktsio.ktor dependency in build.gradle.ktsapplication.yml or application.propertiesApplication.kt main class**Framework**: Spring Boot 3.2.x
**Kotlin**: 1.9.x
**Build Tool**: Gradle (Kotlin DSL)
**Dependencies**:
- Spring WebFlux (reactive)
- Spring Data R2DBC
- Kotlinx Coroutines
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
| 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"
}
| 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
}
| 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
}
}
| 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())
}
| 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 { /* ... */ }
}
}
| 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" }
}
}
## 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
code-reviewer skill: General code qualitykotlin-multiplatform-reviewer skill: KMP server sharingsecurity-scanner skill: API security checksxiaohongshu
technical spec
product ux expert
database patterns
Conduct multi-agent task orchestration and workflow coordination.
Initialize project with Conductor artifacts (product definition,
Expert in web animations, transitions, and motion design using Framer Motion and CSS
Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...
PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.
Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...
Display project status, active tracks, and next actions
Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience