Communitygithub.com

kotlin-patterns

Padrões idiomáticos Kotlin, melhores práticas e convenções para construir aplicações robustas, eficientes e de fácil manutenção com corrotinas, segurança contra nulos e construtores DSL. Use ao escrever ou revisar código Kotlin e quando a estrutura idiomática ou a segurança contra nulos estiver em questão.

O que é kotlin-patterns?

Esta skill fornece um guia abrangente para o desenvolvimento Kotlin idiomático, abrangendo segurança contra nulos, imutabilidade, classes lacradas, corrotinas com concorrência estruturada, funções de extensão, construtores DSL seguros e a DSL do Gradle Kotlin. Ela impõe convenções que melhoram a robustez e a legibilidade do código, ajudando os desenvolvedores a evitar armadilhas comuns, como a desembrulhamento forçado de nulos, estado mutável e aninhamento excessivo de funções de escopo. A skill é particularmente útil ao escrever novos código Kotlin, revisar bases de código existentes ou refatorar para padrões mais idiomáticos. Ela se aplica a diversas versões do Kotlin e ferramentas de ecossistema, sendo compatível com IDEs como IntelliJ IDEA e plataformas como Android e Ktor.

Funciona com~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/kotlin-patterns

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Padrões de Desenvolvimento Kotlin

Padrões idiomáticos Kotlin e melhores práticas para construir aplicações robustas, eficientes e de fácil manutenção.

Quando Usar

  • Escrevendo novo código Kotlin
  • Revisando código Kotlin
  • Refatorando código Kotlin existente
  • Projetando módulos ou bibliotecas Kotlin
  • Configurando builds com Gradle Kotlin DSL

Como Funciona

Esta skill impõe convenções idiomáticas Kotlin em sete áreas principais: segurança contra nulos usando o sistema de tipos e operadores de chamada segura, imutabilidade via val e copy() em classes de dados, classes e interfaces lacradas para hierarquias de tipos exaustivas, concorrência estruturada com corrotinas e Flow, funções de extensão para adicionar comportamento sem herança, construtores DSL seguros usando @DslMarker e lambdas com receptor, e Gradle Kotlin DSL para configuração de build.

Exemplos

Segurança contra nulos com operador Elvis:

fun getUserEmail(userId: String): String {
    val user = userRepository.findById(userId)
    return user?.email ?: "[email protected]"
}

Classe lacrada para resultados exaustivos:

sealed class Result<out T> {
    data class Success<T>(val data: T) : Result<T>()
    data class Failure(val error: AppError) : Result<Nothing>()
    data object Loading : Result<Nothing>()
}

Concorrência estruturada com async/await:

suspend fun fetchUserWithPosts(userId: String): UserProfile =
    coroutineScope {
        val user = async { userService.getUser(userId) }
        val posts = async { postService.getUserPosts(userId) }
        UserProfile(user = user.await(), posts = posts.await())
    }

Princípios Fundamentais

1. Segurança contra Nulos

O sistema de tipos do Kotlin distingue tipos anuláveis e não anuláveis. Aproveite-o ao máximo.

// Bom: Use tipos não anuláveis por padrão
fun getUser(id: String): User {
    return userRepository.findById(id)
        ?: throw UserNotFoundException("Usuário $id não encontrado")
}

// Bom: Chamadas seguras e operador Elvis
fun getUserEmail(userId: String): String {
    val user = userRepository.findById(userId)
    return user?.email ?: "[email protected]"
}

// Ruim: Desembrulhamento forçado de tipos anuláveis
fun getUserEmail(userId: String): String {
    val user = userRepository.findById(userId)
    return user!!.email // Lança NPE se for nulo
}

2. Imutabilidade por Padrão

Prefira val em vez de var, coleções imutáveis em vez de mutáveis.

// Bom: Dados imutáveis
data class User(
    val id: String,
    val name: String,
    val email: String,
)

// Bom: Transforme com copy()
fun updateEmail(user: User, newEmail: String): User =
    user.copy(email = newEmail)

// Bom: Coleções imutáveis
val users: List<User> = listOf(user1, user2)
val filtered = users.filter { it.email.isNotBlank() }

// Ruim: Estado mutável
var currentUser: User? = null // Evite estado global mutável
val mutableUsers = mutableListOf<User>() // Evite a menos que seja realmente necessário

3. Corpos de Expressão e Funções de Expressão Única

Use corpos de expressão para funções concisas e legíveis.

// Bom: Corpo de expressão
fun isAdult(age: Int): Boolean = age >= 18

fun formatFullName(first: String, last: String): String =
    "$first $last".trim()

fun User.displayName(): String =
    name.ifBlank { email.substringBefore('@') }

// Bom: When como expressão
fun statusMessage(code: Int): String = when (code) {
    200 -> "OK"
    404 -> "Não Encontrado"
    500 -> "Erro Interno do Servidor"
    else -> "Status desconhecido: $code"
}

// Ruim: Corpo de bloco desnecessário
fun isAdult(age: Int): Boolean {
    return age >= 18
}

4. Classes de Dados para Objetos de Valor

Use classes de dados para tipos que primariamente armazenam dados.

// Bom: Classe de dados com copy, equals, hashCode, toString
data class CreateUserRequest(
    val name: String,
    val email: String,
    val role: Role = Role.USER,
)

// Bom: Classe de valor para segurança de tipos (sobrecarga zero em tempo de execução)
@JvmInline
value class UserId(val value: String) {
    init {
        require(value.isNotBlank()) { "UserId não pode estar em branco" }
    }
}

@JvmInline
value class Email(val value: String) {
    init {
        require('@' in value) { "Email inválido: $value" }
    }
}

fun getUser(id: UserId): User = userRepository.findById(id)

Classes e Interfaces Lacradas

Modelando Hierarquias Restritas

// Bom: Classe lacrada para when exaustivo
sealed class Result<out T> {
    data class Success<T>(val data: T) : Result<T>()
    data class Failure(val error: AppError) : Result<Nothing>()
    data object Loading : Result<Nothing>()
}

fun <T> Result<T>.getOrNull(): T? = when (this) {
    is Result.Success -> data
    is Result.Failure -> null
    is Result.Loading -> null
}

fun <T> Result<T>.getOrThrow(): T = when (this) {
    is Result.Success -> data
    is Result.Failure -> throw error.toException()
    is Result.Loading -> throw IllegalStateException("Ainda carregando")
}

Interfaces Lacradas para Respostas de API

sealed interface ApiError {
    val message: String

    data class NotFound(override val message: String) : ApiError
    data class Unauthorized(override val message: String) : ApiError
    data class Validation(
        override val message: String,
        val field: String,
    ) : ApiError
    data class Internal(
        override val message: String,
        val cause: Throwable? = null,
    ) : ApiError
}

fun ApiError.toStatusCode(): Int = when (this) {
    is ApiError.NotFound -> 404
    is ApiError.Unauthorized -> 401
    is ApiError.Validation -> 422
    is ApiError.Internal -> 500
}

Funções de Escopo

Quando Usar Cada Uma

// let: Transformar resultado anulável ou escopado
val length: Int? = name?.let { it.trim().length }

// apply: Configurar um objeto (retorna o objeto)
val user = User().apply {
    name = "Alice"
    email = "[email protected]"
}

// also: Efeitos colaterais (retorna o objeto)
val user = createUser(request).also { logger.info("Usuário criado: ${it.id}") }

// run: Executar um bloco com receptor (retorna resultado)
val result = connection.run {
    prepareStatement(sql)
    executeQuery()
}

// with: Forma não extensivel de run
val csv = with(StringBuilder()) {
    appendLine("nome,email")
    users.forEach { appendLine("${it.name},${it.email}") }
    toString()
}

Anti-Padrões

// Ruim: Aninhamento de funções de escopo
user?.let { u ->
    u.address?.let { addr ->
        addr.city?.let { city ->
            println(city) // Dificil de ler
        }
    }
}

// Bom: Encadeie chamadas seguras em vez disso
val city = user?.address?.city
city?.let { println(it) }

Funções de Extensão

Adicionando Funcionalidade Sem Herança

// Bom: Extensões específicas do domínio
fun String.toSlug(): String =
    lowercase()
        .replace(Regex("[^a-z0-9\\s-]"), "")
        .replace(Regex("\\s+"), "-")
        .trim('-')

fun Instant.toLocalDate(zone: ZoneId = ZoneId.systemDefault()): LocalDate =
    atZone(zone).toLocalDate()

// Bom: Extensões de coleção
fun <T> List<T>.second(): T = this[1]

fun <T> List<T>.secondOrNull(): T? = getOrNull(1)

// Bom: Extensões escopadas (não poluindo o namespace global)
class UserService {
    private fun User.isActive(): Boolean =
        status == Status.ACTIVE && lastLogin.isAfter(Instant.now().minus(30, ChronoUnit.DAYS))

    fun getActiveUsers(): List<User> = userRepository.findAll().filter { it.isActive() }
}

Corrotinas

Concorrência Estruturada

// Bom: Concorrência estruturada com coroutineScope
suspend fun fetchUserWithPosts(userId: String): UserProfile =
    coroutineScope {
        val userDeferred = async { userService.getUser(userId) }
        val postsDeferred = async { postService.getUserPosts(userId) }

        UserProfile(
            user = userDeferred.await(),
            posts = postsDeferred.await(),
        )
    }

// Bom: supervisorScope quando filhos podem falhar independentemente
suspend fun fetchDashboard(userId: String): Dashboard =
    supervisorScope {
        val user = async { userService.getUser(userId) }
        val notifications = async { notificationService.getRecent(userId) }
        val recommendations = async { recommendationService.getFor(userId) }

        Dashboard(
            user = user.await(),
            notifications = try {
                notifications.await()
            } catch (e: CancellationException) {
                throw e
            } catch (e: Exception) {
                emptyList()
            },
            recommendations = try {
                recommendations.await()
            } catch (e: CancellationException) {
                throw e
            } catch (e: Exception) {
                emptyList()
            },
        )
    }

Flow para Streams Reativas

// Bom: Flow frio com tratamento adequado de erros
fun observeUsers(): Flow<List<User>> = flow {
    while (currentCoroutineContext().isActive) {
        val users = userRepository.findAll()
        emit(users)
        delay(5.seconds)
    }
}.catch { e ->
    logger.error("Erro ao observar usuários", e)
    emit(emptyList())
}

// Bom: Operadores Flow
fun searchUsers(query: Flow<String>): Flow<List<User>> =
    query
        .debounce(300.milliseconds)
        .distinctUntilChanged()
        .filter { it.length >= 2 }
        .mapLatest { q -> userRepository.search(q) }
        .catch { emit(emptyList()) }

Cancelamento e Limpeza

// Bom: Respeitar cancelamento
suspend fun processItems(items: List<Item>) {
    items.forEach { item ->
        ensureActive() // Verificar cancelamento antes de trabalho caro
        processItem(item)
    }
}

// Bom: Limpeza com try/finally
suspend fun acquireAndProcess() {
    val resource = acquireResource()
    try {
        resource.process()
    } finally {
        withContext(NonCancellable) {
            resource.release() // Sempre liberar, mesmo em cancelamento
        }
    }
}

Delegação

Delegação de Propriedade

// Inicialização preguiçosa
val expensiveData: List<User> by lazy {
    userRepository.findAll()
}

// Propriedade observável
var name: String by Delegates.observable("inicial") { _, old, new ->
    logger.info("Nome mudou de '$old' para '$new'")
}

// Propriedades baseadas em mapa
class Config(private val map: Map<String, Any?>) {
    val host: String by map
    val port: Int by map
    val debug: Boolean by map
}

val config = Config(mapOf("host" to "localhost", "port" to 8080, "debug" to true))

Delegação de Interface

// Bom: Delegar implementação de interface
class LoggingUserRepository(
    private val delegate: UserRepository,
    private val logger: Logger,
) : UserRepository by delegate {
    // Sobrescreva apenas o que precisa para adicionar logging
    override suspend fun findById(id: String): User? {
        logger.info("Buscando usuário pelo id: $id")
        return delegate.findById(id).also {
            logger.info("Usuário encontrado: ${it?.name ?: "null"}")
        }
    }
}

Construtores DSL

Construtores Seguros

// Bom: DSL com @DslMarker
@DslMarker
annotation class HtmlDsl

@HtmlDsl
class HTML {
    private val children = mutableListOf<Element>()

    fun head(init: Head.() -> Unit) {
        children += Head().apply(init)
    }

    fun body(init: Body.() -> Unit) {
        children += Body().apply(init)
    }

    override fun toString(): String = children.joinToString("\n")
}

fun html(init: HTML.() -> Unit): HTML = HTML().apply(init)

// Uso
val page = html {
    head { title("Minha Página") }
    body {
        h1("Bem-vindo")
        p("Olá, Mundo!")
    }
}

DSL de Configuração

data class ServerConfig(
    val host: String = "0.0.0.0",
    val port: Int = 8080,
    val ssl: SslConfig? = null,
    val database: DatabaseConfig? = null,
)

data class SslConfig(val certPath: String, val keyPath: String)
data class DatabaseConfig(val url: String, val maxPoolSize: Int = 10)

class ServerConfigBuilder {
    var host: String = "0.0.0.0"
    var port: Int = 8080
    private var ssl: SslConfig? = null
    private var database: DatabaseConfig? = null

    fun ssl(certPath: String, keyPath: String) {
        ssl = SslConfig(certPath, keyPath)
    }

    fun database(url: String, maxPoolSize: Int = 10) {
        database = DatabaseConfig(url, maxPoolSize)
    }

    fun build(): ServerConfig = ServerConfig(host, port, ssl, database)
}

fun serverConfig(init: ServerConfigBuilder.() -> Unit): ServerConfig =
    ServerConfigBuilder().apply(init).build()

// Uso
val config = serverConfig {
    host = "0.0.0.0"
    port = 443
    ssl("/certs/cert.pem", "/certs/key.pem")
    database("jdbc:postgresql://localhost:5432/mydb", maxPoolSize = 20)
}

Sequências para Avaliação Preguiçosa

// Bom: Use sequências para grandes coleções com múltiplas operações
val result = users.asSequence()
    .filter { it.isActive }
    .map { it.email }
    .filter { it.endsWith("@company.com") }
    .take(10)
    .toList()

// Bom: Gere sequências infinitas
val fibonacci: Sequence<Long> = sequence {
    var a = 0L
    var b = 1L
    while (true) {
        yield(a)
        val next = a + b
        a = b
        b = next
    }
}

val first20 = fibonacci.take(20).toList()

Gradle Kotlin DSL

Configuração build.gradle.kts

// Verifique as versões mais recentes: https://kotlinlang.org/docs/releases.html
plugins {
    kotlin("jvm") version "2.3.10"
    kotlin("plugin.serialization") version "2.3.10"
    id("io.ktor.plugin") version "3.4.0"
    id("org.jetbrains.kotlinx.kover") version "0.9.7"
    id("io.gitlab.arturbosch.detekt") version "1.23.8"
}

group = "com.example"
version = "1.0.0"

kotlin {
    jvmToolchain(21)
}

dependencies {
    // Ktor
    implementation("io.ktor:ktor-server-core:3.4.0")
    implementation("io.ktor:ktor-server-netty:3.4.0")
    implementation("io.ktor:ktor-server-content-negotiation:3.4.0")
    implementation("io.ktor:ktor-serialization-kotlinx-json:3.4.0")

    // Exposed
    implementation("org.jetbrains.exposed:exposed-core:1.

Nota: O conteúdo foi truncado no original.

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

Habilidades Relacionadas