internal/skills/content/kotlin-guide/SKILL.md
Kotlin language guardrails, patterns, and best practices for AI-assisted development. Use when working with Kotlin files (.kt, .kts), build.gradle.kts, or when the user mentions Kotlin. Provides null safety patterns, coroutine guidelines, data class conventions, and testing standards specific to this project's coding standards.
npx skillsauth add ar4mirez/samuel kotlin-guideInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Applies to: Kotlin 1.9+, JVM 17+, Coroutines, Android, Server-side
val over var, immutable collections over mutablebuild.gradle.kts)./gradlew dependencies to audit transitive dependenciesktlint before every commit (format + check)detekt for static analysis (complexity, code smells)com.example.userservice)PascalCase | Functions/properties: camelCase | Constants: SCREAMING_SNAKE_CASE!! (not-null assertion) in production code?.), elvis (?:), and smart casts insteadrequireNotNull() or checkNotNull() with meaningful messages over !!?.let { } for nullable transformations, not nested if checks// BAD: crashes at runtime
val length = name!!.length
// GOOD: safe call with fallback
val length = name?.length ?: 0
// GOOD: explicit contract with clear error
val validName = requireNotNull(name) { "User name must not be null for ID=$id" }
coroutineScope, supervisorScope)GlobalScope in production codewithContext(Dispatchers.IO) for blocking I/O operationsCancellationException correctly (rethrow, never swallow)withTimeout or withTimeoutOrNull for external calls// BAD: unstructured, leaks coroutines
GlobalScope.launch { fetchData() }
// GOOD: structured concurrency, respects parent lifecycle
coroutineScope {
val user = async { userService.getUser(id) }
val orders = async { orderService.getOrders(id) }
UserWithOrders(user.await(), orders.await())
}
StringExtensions.kt, DateExtensions.kt)Any or overly generic types@receiver KDoc tag when purpose is not obviousmyproject/
├── app/ # Application module or main entry
│ └── src/main/kotlin/
├── domain/ # Business logic, entities, use cases
│ └── src/main/kotlin/
│ └── com/example/domain/
│ ├── model/ # Data classes, sealed classes
│ ├── repository/ # Repository interfaces
│ └── usecase/ # Business operations
├── data/ # Data layer implementations
│ └── src/main/kotlin/
│ └── com/example/data/
│ ├── repository/ # Repository implementations
│ ├── remote/ # API clients, DTOs
│ └── local/ # Database, DAOs
├── presentation/ # UI or API controllers
├── build.gradle.kts
├── settings.gradle.kts
└── gradle.properties
domain/ has zero framework dependencies (pure Kotlin)data/ depends on domain/, implements repository interfacespresentation/ depends on domain/, never imports from data/ directlydata class User(
val id: UserId,
val email: Email,
val name: String,
val role: Role = Role.VIEWER,
) {
init {
require(name.isNotBlank()) { "User name must not be blank" }
}
}
// Value classes for type-safe IDs (zero runtime overhead)
@JvmInline
value class UserId(val value: String) {
init { require(value.isNotBlank()) { "UserId must not be blank" } }
}
sealed interface Result<out T> {
data class Success<T>(val data: T) : Result<T>
data class Failure(val error: AppError) : Result<Nothing>
}
sealed class AppError(val message: String) {
data class NotFound(val resource: String, val id: String) :
AppError("$resource with ID $id not found")
data class Validation(val field: String, val reason: String) :
AppError("Validation failed for $field: $reason")
data class Unauthorized(val detail: String = "Authentication required") :
AppError(detail)
}
// Exhaustive when expressions
fun <T> Result<T>.getOrThrow(): T = when (this) {
is Result.Success -> data
is Result.Failure -> throw error.toException()
}
| Function | Context | Returns | Use for |
|----------|---------|---------|---------|
| let | it | Lambda result | Nullable transforms, scoped vars |
| run | this | Lambda result | Compute value using object context |
| with | this | Lambda result | Operate on non-null object |
| apply | this | Object itself | Configure/build an object |
| also | it | Object itself | Side effects (logging, events) |
// apply: configure and return the object
val request = HttpRequest.Builder().apply {
url(endpoint)
header("Authorization", "Bearer $token")
timeout(Duration.ofSeconds(30))
}.build()
// also: side effects without modifying the chain
val savedUser = userRepository.save(newUser).also { user ->
logger.info("Created user: ${user.id}")
}
*Test.kt in src/test/kotlin/ (mirror source package)@Test, @Nested, @DisplayName@ParameterizedTest and @MethodSourcerunTest from kotlinx-coroutines-test for coroutine testsclass UserServiceTest {
private val userRepository = mockk<UserRepository>()
private val eventBus = mockk<EventBus>(relaxed = true)
private val service = UserService(userRepository, eventBus)
@Nested
@DisplayName("createUser")
inner class CreateUser {
@Test
fun `creates user with valid input`() = runTest {
coEvery { userRepository.save(any()) } returns mockUser
val result = service.createUser(validInput)
assertThat(result).isInstanceOf(Result.Success::class.java)
coVerify { userRepository.save(any()) }
coVerify { eventBus.publish(any<UserCreatedEvent>()) }
}
@Test
fun `fails with blank name`() = runTest {
val result = service.createUser(blankNameInput)
assertThat(result).isInstanceOf(Result.Failure::class.java)
coVerify(exactly = 0) { userRepository.save(any()) }
}
}
}
companion object {
@JvmStatic
fun emailCases() = listOf(
Arguments.of("[email protected]", true),
Arguments.of("invalid-email", false),
Arguments.of("", false),
)
}
@ParameterizedTest(name = "email \"{0}\" valid={1}")
@MethodSource("emailCases")
fun `validates email format`(email: String, expected: Boolean) {
val result = runCatching { Email(email) }
assertThat(result.isSuccess).isEqualTo(expected)
}
./gradlew build # Compile + test + check
./gradlew test # Run all tests
./gradlew test --tests "*.UserServiceTest" # Specific test class
./gradlew koverReport # Coverage report
./gradlew ktlintCheck # Check formatting
./gradlew ktlintFormat # Auto-fix formatting
./gradlew detekt # Static analysis
./gradlew dependencies # Dependency tree
// build.gradle.kts
plugins {
kotlin("jvm") version "1.9.22"
id("org.jlleitschuh.gradle.ktlint") version "12.1.0"
id("io.gitlab.arturbosch.detekt") version "1.23.4"
id("org.jetbrains.kotlinx.kover") version "0.7.5"
}
kotlin { jvmToolchain(17) }
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2")
testImplementation(kotlin("test"))
testImplementation("io.mockk:mockk:1.13.9")
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.8.0")
testImplementation("org.assertj:assertj-core:3.25.1")
}
detekt {
config.setFrom("detekt.yml")
buildUponDefaultConfig = true
}
kover {
verify { rule { minBound(80) } }
}
# detekt.yml
complexity:
LongMethod:
threshold: 50
CyclomaticComplexMethod:
threshold: 10
LargeClass:
threshold: 300
LongParameterList:
functionThreshold: 5
constructorThreshold: 8
style:
ForbiddenComment:
values:
- "TODO(?!\\(#\\d+\\))" # TODOs require issue reference
MagicNumber:
ignoreNumbers: ["-1", "0", "1", "2"]
MaxLineLength:
maxLineLength: 120
potential-bugs:
UnsafeCast:
active: true
For detailed patterns and examples, see:
development
Zig language guardrails, patterns, and best practices for AI-assisted development. Use when working with Zig files (.zig), build.zig, or when the user mentions Zig. Provides comptime patterns, allocator conventions, C interop guidelines, and testing standards specific to this project's coding standards.
tools
WordPress framework guardrails, patterns, and best practices for AI-assisted development. Use when working with WordPress projects, or when the user mentions WordPress. Provides theme development, plugin architecture, REST API, blocks, and security guidelines.
tools
Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs. Use when testing web apps, automating browser interactions, or debugging frontend issues.
tools
Suite of tools for creating elaborate, multi-component web applications using modern frontend technologies (React, Tailwind CSS, shadcn/ui). Use for complex projects requiring state management, routing, or shadcn/ui components - not for simple single-file HTML/JSX pages.