mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-03-30 13:43:26 +08:00
87 lines
2.6 KiB
Markdown
87 lines
2.6 KiB
Markdown
---
|
|
paths:
|
|
- "**/*.kt"
|
|
- "**/*.kts"
|
|
---
|
|
# Kotlin Coding Style
|
|
|
|
> This file extends [common/coding-style.md](../common/coding-style.md) with Kotlin-specific content.
|
|
|
|
## Formatting
|
|
|
|
- **ktlint** or **Detekt** for style enforcement
|
|
- Official Kotlin code style (`kotlin.code.style=official` in `gradle.properties`)
|
|
|
|
## Immutability
|
|
|
|
- Prefer `val` over `var` — default to `val` and only use `var` when mutation is required
|
|
- Use `data class` for value types; use immutable collections (`List`, `Map`, `Set`) in public APIs
|
|
- Copy-on-write for state updates: `state.copy(field = newValue)`
|
|
|
|
## Naming
|
|
|
|
Follow Kotlin conventions:
|
|
- `camelCase` for functions and properties
|
|
- `PascalCase` for classes, interfaces, objects, and type aliases
|
|
- `SCREAMING_SNAKE_CASE` for constants (`const val` or `@JvmStatic`)
|
|
- Prefix interfaces with behavior, not `I`: `Clickable` not `IClickable`
|
|
|
|
## Null Safety
|
|
|
|
- Never use `!!` — prefer `?.`, `?:`, `requireNotNull()`, or `checkNotNull()`
|
|
- Use `?.let {}` for scoped null-safe operations
|
|
- Return nullable types from functions that can legitimately have no result
|
|
|
|
```kotlin
|
|
// BAD
|
|
val name = user!!.name
|
|
|
|
// GOOD
|
|
val name = user?.name ?: "Unknown"
|
|
val name = requireNotNull(user) { "User must be set before accessing name" }.name
|
|
```
|
|
|
|
## Sealed Types
|
|
|
|
Use sealed classes/interfaces to model closed state hierarchies:
|
|
|
|
```kotlin
|
|
sealed interface UiState<out T> {
|
|
data object Loading : UiState<Nothing>
|
|
data class Success<T>(val data: T) : UiState<T>
|
|
data class Error(val message: String) : UiState<Nothing>
|
|
}
|
|
```
|
|
|
|
Always use exhaustive `when` with sealed types — no `else` branch.
|
|
|
|
## Extension Functions
|
|
|
|
Use extension functions for utility operations, but keep them discoverable:
|
|
- Place in a file named after the receiver type (`StringExt.kt`, `FlowExt.kt`)
|
|
- Keep scope limited — don't add extensions to `Any` or overly generic types
|
|
|
|
## Scope Functions
|
|
|
|
Use the right scope function:
|
|
- `let` — null check + transform: `user?.let { greet(it) }`
|
|
- `run` — compute a result using receiver: `service.run { fetch(config) }`
|
|
- `apply` — configure an object: `builder.apply { timeout = 30 }`
|
|
- `also` — side effects: `result.also { log(it) }`
|
|
- Avoid deep nesting of scope functions (max 2 levels)
|
|
|
|
## Error Handling
|
|
|
|
- Use `Result<T>` or custom sealed types
|
|
- Use `runCatching {}` for wrapping throwable code
|
|
- Never catch `CancellationException` — always rethrow it
|
|
- Avoid `try-catch` for control flow
|
|
|
|
```kotlin
|
|
// BAD — using exceptions for control flow
|
|
val user = try { repository.getUser(id) } catch (e: NotFoundException) { null }
|
|
|
|
// GOOD — nullable return
|
|
val user: User? = repository.findUser(id)
|
|
```
|