---
title: "Detekt e ktlint em Kotlin: Qualidade de Código em 2026 | Kotlin Brasil"
url: "https://kotlin.dev.br/blog/detekt-ktlint-kotlin-qualidade-2026/"
markdown_url: "https://kotlin.dev.br/blog/detekt-ktlint-kotlin-qualidade-2026.MD"
description: "Configure detekt, ktlint e checks de qualidade em projetos Kotlin, com Gradle, CI, regras úteis e um caminho realista para times em 2026."
date: "2026-05-18"
author: "Karina Melo"
---

# Detekt e ktlint em Kotlin: Qualidade de Código em 2026 | Kotlin Brasil

Configure detekt, ktlint e checks de qualidade em projetos Kotlin, com Gradle, CI, regras úteis e um caminho realista para times em 2026.


Qualidade de código em Kotlin não depende só de code review. Review humano é essencial para arquitetura, clareza de regra de negócio e decisões de produto, mas não deveria gastar energia discutindo import fora de ordem, função longa, `MagicNumber`, complexidade ciclomática ou formatação inconsistente. Para isso existem ferramentas automáticas.

Em 2026, a combinação mais prática para projetos Kotlin continua sendo **ktlint** para estilo e formatação, **detekt** para análise estática e um pipeline de **CI/CD** que bloqueia regressões antes do merge. Esse trio funciona para Android, backend com [Ktor](/blog/ktor-criando-apis-kotlin/), [Spring Boot](/blog/kotlin-spring-boot/), bibliotecas JVM e projetos [Kotlin Multiplatform](/blog/kotlin-multiplatform/).

A diferença entre um setup útil e um setup irritante está na calibragem. Se o time ativa todas as regras de uma vez em um projeto legado, a ferramenta vira ruído. Se deixa tudo permissivo demais, vira decoração no `build.gradle.kts`. O caminho bom é começar com regras que pegam problemas reais, automatizar o que pode ser corrigido sozinho e evoluir a rigidez aos poucos.

## ktlint, detekt e testes resolvem problemas diferentes

Antes de configurar plugins, vale separar responsabilidades.

**ktlint** cuida de formatação e estilo. Ele aplica convenções próximas do estilo oficial do Kotlin: indentação, espaços, quebras de linha, imports e organização visual. É a ferramenta que evita discussões improdutivas sobre como o código “deveria parecer”.

**detekt** faz análise estática. Ele procura code smells, complexidade excessiva, nomes ruins, blocos grandes demais, exceções engolidas, duplicação conceitual e padrões que costumam dificultar manutenção. Ele não substitui testes, mas ajuda a identificar código que tende a quebrar ou ficar caro de modificar.

**Testes** verificam comportamento. Um teste com [JUnit 5 e MockK](/blog/kotlin-testes-junit5-mockk-guia/) responde se uma regra funciona; o [guia completo de testes em Kotlin](/guias/guia-testes-kotlin/) ajuda a decidir onde entram coroutines, Flow, integração e Android; detekt responde se o formato do código está saudável; ktlint responde se o estilo está consistente. Quando as três camadas trabalham juntas, o review fica mais estratégico.

## Configurando ktlint com Gradle

O jeito mais comum de usar ktlint em projetos Kotlin é via plugin Gradle. Em um projeto JVM ou Android, adicione o plugin no `build.gradle.kts` raiz:

```kotlin
plugins {
    id("org.jlleitschuh.gradle.ktlint") version "12.1.1"
}

ktlint {
    version.set("1.3.1")
    android.set(false)
    outputToConsole.set(true)
    coloredOutput.set(true)
    ignoreFailures.set(false)
}
```

Em projetos Android, ajuste `android.set(true)` para habilitar algumas convenções específicas. Depois, rode:

```bash
./gradlew ktlintCheck
./gradlew ktlintFormat
```

O `ktlintCheck` deve rodar no CI e falhar quando houver problema. O `ktlintFormat` pode rodar localmente antes do commit para corrigir o que é automático. Uma prática simples é documentar no README: “antes de abrir PR, rode `./gradlew ktlintFormat test`”.

Para times maiores, também vale integrar com pre-commit hook. Mas cuidado: hook local ajuda, não substitui CI. Desenvolvedor pode pular hook; pipeline não deveria pular qualidade.

## Configurando detekt sem travar o time

O detekt também entra pelo Gradle:

```kotlin
plugins {
    id("io.gitlab.arturbosch.detekt") version "1.23.7"
}

detekt {
    buildUponDefaultConfig = true
    allRules = false
    config.setFrom(files("config/detekt/detekt.yml"))
    baseline = file("config/detekt/baseline.xml")
}

dependencies {
    detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:1.23.7")
}
```

Crie a configuração inicial:

```bash
./gradlew detektGenerateConfig
mkdir -p config/detekt
mv config/detekt/detekt.yml config/detekt/detekt.yml
```

Se o projeto já tem muito código, gere um baseline:

```bash
./gradlew detektBaseline
```

O baseline registra problemas existentes para que o CI bloqueie apenas problemas novos. Isso é importante em sistemas reais. Em vez de tentar “arrumar tudo” em uma semana, o time impede deterioração e agenda limpezas graduais.

## Regras que valem ativar cedo

Nem toda regra do detekt tem o mesmo valor. Algumas pegam problemas práticos rapidamente:

- `LongMethod`: funções grandes demais, difíceis de testar.
- `LongParameterList`: construtores e métodos com dependência demais.
- `ComplexMethod`: lógica condicional excessiva.
- `TooGenericExceptionCaught`: `catch (Exception)` escondendo falhas importantes.
- `SwallowedException`: exceção capturada e ignorada.
- `MagicNumber`: números sem nome prejudicando intenção do código.
- `NestedBlockDepth`: muitos `if`, `when` e loops aninhados.
- `ForbiddenComment`: `TODO`, `FIXME` e marcadores que não deveriam chegar em produção.

Um trecho problemático que detekt apontaria:

```kotlin
fun calcularTaxa(cliente: Cliente, pedido: Pedido): Double {
    var taxa = 0.0
    if (cliente.ativo) {
        if (pedido.valor > 1000) {
            if (cliente.plano == "premium") {
                taxa = 0.02
            } else {
                taxa = 0.05
            }
        } else {
            taxa = 0.08
        }
    }
    return taxa
}
```

O código funciona, mas a intenção está enterrada em condicionais. Uma versão mais idiomática separa regras:

```kotlin
fun calcularTaxa(cliente: Cliente, pedido: Pedido): Double = when {
    !cliente.ativo -> 0.0
    cliente.plano == Plano.PREMIUM && pedido.valor > LIMITE_PEDIDO_ALTO -> TAXA_PREMIUM
    pedido.valor > LIMITE_PEDIDO_ALTO -> TAXA_PEDIDO_ALTO
    else -> TAXA_PADRAO
}

private const val LIMITE_PEDIDO_ALTO = 1_000.0
private const val TAXA_PREMIUM = 0.02
private const val TAXA_PEDIDO_ALTO = 0.05
private const val TAXA_PADRAO = 0.08
```

Além de satisfazer regras, a segunda versão é mais fácil de revisar, testar e modificar.

## Pipeline de CI para GitHub Actions

Em um projeto Kotlin, o pipeline mínimo deve compilar, rodar testes, checar formatação e executar análise estática. Um exemplo com GitHub Actions:

```yaml
name: Kotlin Quality

on:
  pull_request:
  push:
    branches: [main]

jobs:
  quality:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-java@v4
        with:
          distribution: temurin
          java-version: 21

      - uses: gradle/actions/setup-gradle@v4

      - name: Check formatting
        run: ./gradlew ktlintCheck

      - name: Static analysis
        run: ./gradlew detekt

      - name: Tests
        run: ./gradlew test
```

Se o projeto usa Android, substitua ou complemente com tasks como `testDebugUnitTest`, `lintDebug` e build variants relevantes. Para backend, inclua testes de integração quando fizer sentido. O guia de [CI/CD para Kotlin](/guias/guia-kotlin-ci-cd/) aprofunda pipelines com GitHub Actions, GitLab CI e Jenkins.

Uma otimização útil é publicar relatórios como artefatos quando algo falha:

```yaml
      - uses: actions/upload-artifact@v4
        if: failure()
        with:
          name: quality-reports
          path: |
            build/reports/
            **/build/reports/
```

Assim, quem abriu o PR consegue baixar HTML de testes, relatório do detekt e saída de lint sem reproduzir tudo localmente.

## Como aplicar em projeto legado

O erro clássico em projeto legado é transformar a primeira execução em uma lista de 900 problemas. Ninguém vai corrigir isso com qualidade no meio de uma sprint. O melhor plano é incremental:

1. Adicione ktlint e rode `ktlintFormat` em um commit isolado.
2. Adicione detekt com baseline para problemas existentes.
3. Bloqueie novos problemas no CI.
4. Escolha uma regra por semana para endurecer.
5. Remova partes do baseline conforme módulos forem refatorados.

Separar commits importa. Um commit só de formatação é chato, mas fácil de revisar. Misturar formatação com mudança de regra de negócio cria diff barulhento e aumenta risco.

Para Android, comece por módulos novos ou mais ativos. Para backend, comece por camadas de domínio e serviços com mudança frequente. O objetivo não é “zerar relatório” por vaidade; é reduzir custo de manutenção onde o time mais mexe.

## Convenções de time que evitam briga

Ferramenta boa precisa de acordo social. Algumas decisões devem estar explícitas:

- Qual versão de Kotlin, Gradle, detekt e ktlint o projeto usa.
- Se `ktlintFormat` pode alterar código automaticamente antes do commit.
- Quais regras do detekt são bloqueantes e quais são apenas aviso.
- Como pedir exceção para uma regra em caso específico.
- Quem revisa mudanças no `detekt.yml`.

Use supressões com parcimônia:

```kotlin
@Suppress("LongMethod")
fun migracaoTemporaria() {
    // código transitório, com issue vinculada para remoção
}
```

A supressão deve explicar o motivo. Se o time começa a espalhar `@Suppress` sem justificativa, a ferramenta perdeu autoridade.

## Relação com arquitetura e performance

Detekt e ktlint não decidem arquitetura. Eles não sabem se você deveria usar [Ktor ou Spring Boot](/comparacoes/ktor-vs-spring-boot/), se vale migrar para [coroutines avançadas](/blog/coroutines-avancadas-structured-concurrency-kotlin/) ou se um módulo deveria virar microsserviço. Mas eles deixam sinais objetivos: classes grandes, acoplamento alto, funções instáveis e complexidade concentrada.

Esses sinais ajudam o time a priorizar refatorações. Se uma classe aparece em todo relatório, muda toda semana e concentra bugs, ela é candidata a extração, testes melhores ou redesenho. Em backends de alta concorrência, o mesmo raciocínio vale para serviços assíncronos com [Flow](/blog/kotlin-flow/) e mensageria com [Kafka e RabbitMQ](/blog/kotlin-kafka-rabbitmq-mensageria-2026/).

Também existe aprendizado fora do ecossistema Kotlin. Times que trabalham com Go costumam automatizar `gofmt`, `go vet` e testes desde cedo; vale comparar essa disciplina com os guias de <a href="https://golang.com.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'golang.com.br' })">Go para backend no Brasil</a>. A tecnologia muda, mas a ideia é a mesma: tirar decisões repetitivas do review humano.

## Checklist recomendado para 2026

Para um projeto Kotlin novo, eu começaria assim:

- Kotlin e JDK atualizados conforme suporte do projeto.
- Gradle com [Kotlin DSL](/tutoriais/kotlin-gradle-tutorial/).
- ktlint rodando localmente e no CI.
- detekt com configuração versionada em `config/detekt/detekt.yml`.
- Testes unitários obrigatórios em PR.
- Relatórios publicados como artefatos quando o pipeline falhar.
- Baseline apenas se o projeto já for legado.
- Revisão mensal das regras que geram mais ruído.

Para um projeto existente, não tente virar “perfeito” em uma sexta-feira. Automatize primeiro, bloqueie regressão depois e só então endureça o padrão. Qualidade sustentável é mais parecida com higiene diária do que com mutirão heroico.

## Conclusão

Detekt e ktlint são ferramentas pequenas perto das decisões de produto, mas mudam a rotina de um time Kotlin. Elas reduzem ruído no review, tornam padrões explícitos, ajudam pessoas novas a entender o estilo do projeto e criam uma base confiável para evoluir código sem medo.

O segredo é usar automação como apoio, não como burocracia. ktlint deve corrigir o que é mecânico. detekt deve apontar riscos reais. O CI deve proteger a branch principal. E o time deve reservar energia humana para o que ferramenta nenhuma entende bem: clareza de domínio, experiência do usuário, trade-offs de arquitetura e entrega de valor.
