---
name: kotlin-rules
description: "Kotlin coding rules: style, patterns, security, testing. Triggers: .kt, .kts, build.gradle.kts, Ktor, Jetpack Compose, coroutines, kotlinx."
effort: medium
user-invocable: false
allowed-tools: Read
---

# Kotlin Rules

These rules come from `app/rules/kotlin/` in ai-toolkit. They cover
the project's standards for coding style, frameworks, patterns,
security, and testing in Kotlin. Apply them when writing or
reviewing Kotlin code.

# Kotlin Coding Style

## Naming
- PascalCase: classes, interfaces, objects, type aliases, enum entries.
- camelCase: functions, properties, local variables, parameters.
- UPPER_SNAKE: compile-time constants (`const val`), top-level `val` constants.
- Backing properties: prefix with `_` (`private val _items`, `val items: List<T>`).
- Package names: lowercase, no underscores (`com.company.project.feature`).

## Null Safety
- Use nullable types only when nullability is semantically meaningful.
- Prefer `?.let { }`, `?:` (Elvis), and safe calls over `!!`.
- Never use `!!` except in tests or when null is truly impossible.
- Use `requireNotNull()` and `require()` for preconditions at public API boundaries.
- Use `checkNotNull()` and `check()` for state assertions.

## Data Classes
- Use `data class` for DTOs, value objects, and state containers.
- Use `copy()` for immutable updates. Avoid mutable `var` in data classes.
- Use `sealed class` / `sealed interface` for restricted hierarchies.
- Use `value class` (inline class) for type-safe wrappers with zero overhead.
- Use `object` for singletons and namespace-like utility groupings.

## Functions
- Use expression body (`= expr`) for single-expression functions.
- Use named arguments for functions with >2 parameters of the same type.
- Use default parameter values instead of overloaded functions.
- Use extension functions to add behavior without inheritance.
- Use `suspend` functions for async operations, not callbacks.

## Collections
- Prefer `listOf`, `mapOf`, `setOf` (immutable) over `mutableListOf`.
- Use collection operators: `map`, `filter`, `groupBy`, `associate`.
- Use `sequence {}` for lazy evaluation on large collections.
- Prefer `firstOrNull()` over `first()` for safe access.
- Use destructuring: `val (name, age) = user`.

## Scope Functions
- `let`: null-safe chaining and local scoping.
- `apply`: configure object after creation.
- `also`: side effects (logging, validation) in chains.
- `run`: compute a result using receiver's context.
- `with`: multiple operations on an object without chaining.
- Avoid nesting scope functions more than 1 level deep.

## Formatting
- Use ktlint or detekt for automated formatting and linting.
- Use trailing commas in multi-line parameter/argument lists.
- Max line length: 120 characters (Kotlin convention).
- Use `when` expression over if-else chains for 3+ branches.

# Kotlin Frameworks

## Ktor (Server)
- Use routing DSL: `routing { get("/users") { call.respond(users) } }`.
- Use `install()` for plugins: ContentNegotiation, Authentication, CORS.
- Use `call.receive<T>()` for typed request body parsing with kotlinx.serialization.
- Use `StatusPages` plugin for centralized error handling.
- Use `Routing` with nested `route("/api/v1") { }` blocks for URL grouping.

## Ktor (Client)
- Use `HttpClient` with engine configuration (CIO, OkHttp, Apache).
- Use `install(ContentNegotiation) { json() }` for JSON serialization.
- Use `client.get<T>()` with reified type for typed responses.
- Use `HttpTimeout` plugin for connection and request timeouts.
- Close `HttpClient` when done or use DI lifecycle management.

## Spring Boot (Kotlin)
- Use constructor injection (Kotlin classes are `final` by default).
- Apply `kotlin-spring` plugin for open classes (required for proxying).
- Use `@ConfigurationProperties` with data classes for typed config.
- Use `WebFlux` with coroutines: `coRouter { }` and `suspend` handler functions.
- Use `spring-boot-starter-validation` with `@Valid` on Kotlin data classes.

## Exposed (ORM)
- Use DSL API for type-safe queries: `Users.select { Users.name eq "Ada" }`.
- Use DAO API for Active Record-style: `User.find { Users.age greaterEq 18 }`.
- Wrap database operations in `transaction { }` blocks.
- Use `SchemaUtils.create(Users)` for schema management in development.

## kotlinx.serialization
- Use `@Serializable` annotation on data classes for compile-time serialization.
- Use `@SerialName("field_name")` for JSON field name mapping.
- Use `Json { ignoreUnknownKeys = true }` for lenient deserialization.
- Use polymorphic serialization with `sealed class` and `@Polymorphic`.
- Prefer `kotlinx.serialization` over Jackson for pure Kotlin projects.

## Koin (DI)
- Define modules: `module { single { UserService(get()) } }`.
- Use `by inject<T>()` for lazy injection in Android/Ktor.
- Use `factory { }` for new instance per injection, `single { }` for singleton.
- Use `checkModules()` in tests to verify DI graph completeness.

## Compose (Multiplatform UI)
- Use `@Composable` functions for UI components. Keep them stateless.
- Use `remember { }` and `mutableStateOf()` for local state.
- Hoist state to callers: pass state down, events up.
- Use `LaunchedEffect` for side effects tied to composition lifecycle.
- Use `ViewModel` with `StateFlow` for screen-level state management.

# Kotlin Patterns

## Error Handling
- Use `Result<T>` for operations that can fail without exceptions.
- Use `runCatching { }` to wrap exception-throwing code into `Result`.
- Use `sealed class` hierarchies for domain errors: `sealed class AppError`.
- Prefer `fold()`, `getOrElse()`, `getOrNull()` over `getOrThrow()`.
- Use `require()` / `check()` for preconditions; they throw `IllegalArgumentException` / `IllegalStateException`.

## Coroutines
- Use `suspend` functions for sequential async operations.
- Use `coroutineScope { }` for structured concurrency with parallel work.
- Use `async { }` + `await()` for concurrent independent operations.
- Use `supervisorScope { }` when child failures should not cancel siblings.
- Use `withContext(Dispatchers.IO)` for blocking I/O in coroutine context.
- Use `flow { }` for cold asynchronous streams. Collect in lifecycle-aware scope.

## Flow Patterns
- Use `stateIn()` and `shareIn()` to convert cold flows to hot shared state.
- Use `combine()` to merge multiple flows into derived state.
- Use `flatMapLatest` for search-as-you-type patterns (cancel previous).
- Use `catch { }` operator for upstream error handling in flows.
- Use `flowOn(Dispatchers.IO)` to shift upstream execution context.

## Sealed Hierarchies
- Use `sealed interface` over `sealed class` when no shared state is needed.
- Use `when` expressions exhaustively on sealed types (compiler-enforced).
- Combine sealed types with data classes for typed state machines.
- Use sealed hierarchies for API responses: `Success<T>`, `Error`, `Loading`.

## Delegation
- Use `by lazy { }` for thread-safe lazy initialization.
- Use `by map` for delegated properties backed by a `Map`.
- Use class delegation (`class Foo : Bar by impl`) to favor composition.
- Use `observable` / `vetoable` delegates for reactive property changes.

## Builder Patterns
- Use DSL-style builders with `@DslMarker` annotation to prevent scope leakage.
- Use trailing lambda syntax for configuration blocks.
- Use `apply { }` for inline object configuration without a dedicated builder.
- Use `buildList { }`, `buildMap { }`, `buildString { }` for collection construction.

## Anti-Patterns
- Overusing `!!`: masks null-safety guarantees. Use safe calls or require.
- Nesting scope functions: `foo.let { it.also { ... }.run { } }` -- flatten logic.
- Blocking the main thread: use `withContext(Dispatchers.IO)` for I/O.
- Using `GlobalScope.launch`: leaks coroutines. Use structured concurrency.
- Mutable shared state without synchronization: use `Mutex` or `StateFlow`.

# Kotlin Security

## Input Validation
- Validate all inputs at API boundaries using Bean Validation or manual checks.
- Use `require()` for argument validation: `require(age > 0) { "Age must be positive" }`.
- Use data class `init` blocks for domain validation on construction.
- Never trust client-provided IDs. Verify resource ownership server-side.
- Sanitize strings before using in HTML, SQL, or shell commands.

## Null Safety as Security
- Kotlin's null safety prevents null pointer exceptions. Do not circumvent with `!!`.
- Use `?.` and `?:` chains for safe fallback values at boundaries.
- Treat Java interop as untrusted: platform types can still be null.
- Use `@Nullable` / `@NotNull` annotations on Java code consumed by Kotlin.

## SQL Injection
- Use Exposed DSL or JPA with parameterized queries. Never concatenate input.
- Use `PreparedStatement` if writing raw JDBC.
- Use `CriteriaBuilder` or Exposed conditions for dynamic query construction.
- Audit `@Query(nativeQuery = true)` for parameter interpolation risks.

## Serialization
- Use `kotlinx.serialization` with `@Serializable` for compile-time safety.
- Use `Json { ignoreUnknownKeys = true }` but validate after deserialization.
- Never use Java `ObjectInputStream` for deserialization (RCE risk).
- Restrict polymorphic deserialization to known sealed class subtypes.

## Authentication
- Use Spring Security or Ktor Authentication plugin. Do not roll your own.
- Hash passwords with BCrypt or Argon2. Never store plaintext.
- Use short-lived JWTs (15 min) with refresh token rotation.
- Validate JWT signature, issuer, audience, and expiration on every request.

## Coroutine Security
- Use `withTimeout()` to prevent unbounded coroutine execution (DoS vector).
- Use `Mutex` for critical sections. Do not use `synchronized` in suspend functions.
- Propagate security context through `CoroutineContext` elements.
- Cancel coroutine scopes on authentication failure or session expiry.

## Secrets Management
- Use environment variables or Vault for secrets. Never hardcode.
- Use `@ConfigurationProperties` with injected secrets, not string literals.
- Never log request headers containing Authorization tokens.
- Use separate configuration profiles for dev/staging/prod secrets.

## Dependencies
- Use Dependabot or Renovate for automated dependency updates.
- Run OWASP Dependency-Check or Gradle `dependencyCheckAnalyze`.
- Audit transitive dependencies with `gradle dependencies`.
- Pin dependency versions. Avoid dynamic versions like `1.+`.

## Logging
- Use parameterized logging: `logger.info("User {} logged in", userId)`.
- Never log passwords, tokens, or PII.
- Sanitize user input before logging to prevent log injection.
- Use structured logging (JSON) for machine-parseable audit trails.

# Kotlin Testing

## Framework
- Use JUnit 5 as the test runner.
- Use Kotest for Kotlin-idiomatic BDD-style testing (alternative).
- Use MockK for mocking (Kotlin-native, supports coroutines).
- Use Testcontainers for integration tests with external services.

## File Naming
- Test files: `FooTest.kt` in `src/test/kotlin/` mirroring source package.
- Integration tests: `FooIT.kt` or use `@Tag("integration")`.
- Use `@Nested` inner classes to group related test cases.

## Structure
- Use `@DisplayName` for human-readable test names.
- Use backtick function names for readable test names: `` `returns 404 when user not found` ``.
- Use `@BeforeEach` for per-test setup. Avoid shared mutable state.
- Use `@ParameterizedTest` with `@MethodSource` for table-driven tests.

## MockK
- Use `mockk<UserRepository>()` to create mocks.
- Use `every { mock.find(any()) } returns user` for stubbing.
- Use `coEvery { ... }` and `coVerify { ... }` for coroutine mocking.
- Use `spyk()` for partial mocks on real objects.
- Use `slot<T>()` and `captured` to inspect arguments.
- Clear mocks in `@AfterEach` to prevent state leakage.

## Coroutine Testing
- Use `runTest { }` from `kotlinx-coroutines-test` for coroutine tests.
- Use `TestDispatcher` to control coroutine execution timing.
- Use `advanceUntilIdle()` to complete all pending coroutines.
- Use `turbine` library for testing `Flow` emissions.

## Assertions
- Use AssertJ or Kotest assertions for fluent, readable checks.
- Use `shouldBe`, `shouldThrow`, `shouldContain` (Kotest matchers).
- Use `assertSoftly { }` to collect multiple assertion failures.
- Use `assertThrows<FooException> { ... }` for exception testing.

## Test Data
- Use factory functions for test data: `fun aUser(name: String = "Ada") = User(...)`.
- Use default parameters for minimal test data setup.
- Use `copy()` on data classes for variations of base test objects.
- Use `faker` library for realistic test data generation.

## Spring Integration
- Use `@SpringBootTest` with `@AutoConfigureMockMvc` for API tests.
- Use `@WebMvcTest` for controller-only tests (faster).
- Use `@MockkBean` instead of `@MockBean` for MockK integration.
- Use `@Transactional` on test classes for automatic rollback.
