---
overlay: Kotlin Specialization
parent_agent: Super Coder
description: "Kotlin idioms and coroutine patterns"
---

## KOTLIN-SPECIFIC GUIDELINES

You are working in a **Kotlin** codebase. Apply these principles with zero exceptions.

### Coroutines — Structured Concurrency
- Use `suspend` functions for async operations — never use callbacks when coroutines are available
- Use `CoroutineScope` properly — never use `GlobalScope` in production code
- Use `viewModelScope` (Android) or `CoroutineScope(SupervisorJob())` for scoped work
- Use `withContext(Dispatchers.IO)` for I/O operations, `Dispatchers.Default` for CPU-intensive work
- Use `async/await` for parallel decomposition, `launch` for fire-and-forget
- Handle cancellation: check `isActive` in long-running loops, catch `CancellationException` only to rethrow

### Sealed Classes & Exhaustive When
- Use **sealed classes/interfaces** for restricted type hierarchies (state machines, results, events)
- Use `when` expressions with sealed types — the compiler enforces exhaustive matching
- Use sealed classes for Result types: `sealed class Result<T> { data class Success<T>(val data: T) : Result<T>(); ... }`
- Prefer sealed interfaces (Kotlin 1.5+) over sealed classes when state doesn't need to be stored

### Null Safety — The Kotlin Advantage
- Use `?` for nullable types, non-nullable is the default — respect the type system
- Use `?.` (safe call), `?:` (Elvis), `?.let {}` for null handling
- **Never use `!!`** (not-null assertion) in production code — it's a crash waiting to happen
- Use `requireNotNull()` or `checkNotNull()` with descriptive messages for preconditions
- Use `filterNotNull()` and `mapNotNull()` for collections

### Extension Functions
- Use extension functions for adding behavior to types you don't own
- Keep extensions close to their usage — don't create a giant `Extensions.kt` dump file
- Use extension properties for computed values: `val String.isEmail: Boolean get() = ...`
- Scope extensions: define them in the file/class where they're used, not globally

### Data Classes & Value Objects
- Use `data class` for all DTOs and value objects — automatic `equals()`, `hashCode()`, `copy()`, `toString()`
- Use `copy()` for non-destructive updates: `val updated = user.copy(name = "New")`
- Use `value class` (inline class) for type-safe wrappers: `value class UserId(val value: String)`
- Destructuring: `val (name, email) = user`

### Code Style
- Use expression bodies for simple functions: `fun double(x: Int) = x * 2`
- Use `apply`, `also`, `let`, `run`, `with` scope functions — but don't nest them
- Use `object` for singletons and companion factories
- Use `typealias` for complex generic types: `typealias UserMap = Map<UserId, User>`
- Use `require()` and `check()` for preconditions and state validation

### Naming Conventions
- `camelCase` for functions, properties, variables
- `PascalCase` for classes, interfaces, objects, enums
- `UPPER_SNAKE_CASE` for constants (`const val` or `@JvmField val`)
- Boolean properties: `is*`, `has*`, `can*`
- Factory functions: `PascalCase` matching the return type: `fun User(name: String) = User(name, ...)`

### Testing Considerations
- Use constructor injection for testability
- Use `runTest` (coroutine test) for testing suspend functions
- Use `MockK` or project's mocking framework conventions
- Prefer deterministic code — accept `Clock` parameter instead of `Instant.now()`
