Por que depurar Kotlin é uma arte, e não uma rotina?
Imagine: você escreve um código elegante em Kotlin, usa corrotinas, null-safety e extension-functions. Tudo parece perfeito, até que o aplicativo falha com NullPointerException no lugar mais inesperado. Ou pior — uma corrotina simplesmente "adormece" para sempre, sem deixar rastros. Soa familiar?
Depurar código Kotlin não é apenas encontrar bugs. É a habilidade de "espiar" o funcionamento do seu aplicativo em tempo real, entender como as corrotinas funcionam e encontrar rapidamente gargalos. Neste artigo, abordaremos técnicas avançadas de depuração Kotlin que economizarão horas de estresse e tornarão seu código mais estável.
Ferramentas básicas de depuração Kotlin no IntelliJ IDEA
O IntelliJ IDEA não é apenas uma IDE, é um verdadeiro canivete suíço para o desenvolvedor Kotlin. Vamos começar com o que está na superfície, mas que muitas vezes é usado incorretamente.
Pontos de interrupção: mais do que apenas uma pausa
Um ponto de interrupção comum (line breakpoint) é o clássico. Mas o Kotlin oferece muito mais:
- Conditional breakpoints — são acionados apenas quando uma condição é atendida. Por exemplo, parar quando
userId == 42. - Exception breakpoints — capturam exceções antes que elas caiam no log.
- Method breakpoints — são acionados na entrada e saída de uma função. Ideal para depurar cadeias de chamadas complexas.
Exemplo de configuração de conditional breakpoint:
fun processOrder(order: Order) { // Colocamos um breakpoint e adicionamos a condição: order.amount > 1000 val discount = calculateDiscount(order.amount) // ...}Basta clicar com o botão direito no ponto de interrupção e inserir a condição. Economiza muito tempo!
Depuração de corrotinas: espiando o mundo async
Corrotinas são poderosas, mas a depuração pode se tornar um pesadelo. O IntelliJ IDEA (a partir da versão 2020.3) oferece o Coroutines Debugger. Ele mostra:
- O estado de cada corrotina (Active, Suspended, Completed).
- A pilha de chamadas para cada corrotina.
- Os valores das variáveis locais dentro da corrotina.
import kotlinx.coroutines.*
fun main() = runBlocking { val job1 = launch { delay(1000) println("Job 1 done") } val job2 = launch { delay(500) println("Job 2 done") } // Coloque um breakpoint aqui e veja a aba "Coroutines" job1.join() job2.join()}Dica: ative a opção Show coroutines no painel de depuração — isso vai te salvar de dores de cabeça com código assíncrono.
Técnicas avançadas: depuração sem dor
Agora vamos para as técnicas que diferenciam um iniciante de um profissional.
Registro de log como primeira linha de defesa
Nem sempre é possível colocar um ponto de interrupção (por exemplo, em produção). É aí que entra o registro de log. Mas não apenas println(), e sim um registro de log significativo.
import org.slf4j.LoggerFactory
private val log = LoggerFactory.getLogger(MyService::class.java)
fun fetchUserData(userId: String): User? { log.debug("Buscando dados do usuário para userId: {}", userId) return try { val user = repository.findById(userId) log.info("Usuário encontrado: {}", user?.name) user } catch (e: Exception) { log.error("Falha ao buscar usuário: {}", userId, e) null }}Use níveis de log: TRACE para detalhes, DEBUG para depuração, INFO para eventos importantes. E nunca deixe println() em produção — é falta de educação.
Perfilamento de memória: caça a vazamentos
Kotlin, devido à JVM, está sujeito a vazamentos de memória. Use o Profiler integrado no IntelliJ IDEA