Trilha de Kotlin: o mapa das 9 fases
O caminho de Kotlin do zero ao nível sênior: história, POO, coroutines, JVM, APIs com Spring Boot, persistência e arquitetura, em 9 fases e 59 aulas.
Publicada em · 14 min de leitura
Esta nota é o mapa da trilha de estudos para quem quer atuar com Kotlin no backend, mesmo sem saber nada da linguagem nem de orientação a objetos. A ordem foi reformulada para que cada aula use só o que as anteriores já explicaram: começa pela história da linguagem e pelos primeiros programas, passa por POO, Kotlin idiomático, coroutines e JVM, e termina em APIs com Spring Boot, persistência, arquitetura e um projeto completo. Ela deriva do Curriculo de um Desenvolvedor Kotlin, que define o que o mercado espera de um engenheiro nessa função, e segue a mesma estrutura da Trilha de Go, para que as duas possam ser comparadas lado a lado.
A stack considerada é a do projeto: Spring Boot com Gradle, PostgreSQL com Liquibase, MongoDB e mensageria. Cada linha das tabelas indica o tema de uma aula e a pergunta que ela responde.
O mapa geral da trilha
| Fase | Tema | Aulas | Objetivo prático central | Entrega prática prevista |
|---|---|---|---|---|
| 0 | Contexto | 1 | Entender de onde a linguagem vem | Linha do tempo da linguagem e das decisões de projeto |
| 1 | Primeiros passos | 6 | Escrever os primeiros programas e testá-los | Programa de linha de comando com testes automatizados |
| 2 | POO em Kotlin | 6 | Modelar o domínio com objetos | Modelo de domínio com sealed e nenhum !! |
| 3 | Kotlin idiomático | 8 | Escrever Kotlin que aproveita o compilador | Leitor de arquivos grandes com Sequence |
| 4 | Concorrência e coroutines | 8 | Controlar concorrência e cancelamento | Worker com limite de concorrência e cancelamento limpo |
| 5 | A JVM por baixo | 5 | Diagnosticar latência e memória | Diagnóstico de alocação excessiva com JFR |
| 6 | APIs com Spring Boot | 11 | Construir APIs seguras e com contratos claros | API REST com JWT e consumo de eventos com Kafka |
| 7 | Persistência | 8 | Acessar bancos com pool bem dimensionado | Repositório Postgres com Liquibase e repositório MongoDB com índices |
| 8 | Arquitetura, qualidade e operação | 6 | Estruturar um serviço para durar anos | Serviço completo com Testcontainers, métricas e imagem Docker |
Fase 0: Contexto (1 aulas)
Pergunta central: Por que o Kotlin existe, qual problema a JetBrains queria resolver e o que a linguagem propõe?
| Aula | Pergunta que a aula responde |
|---|---|
| História do Kotlin: por que a linguagem nasceu e qual problema ela resolve | Como a JetBrains criou o Kotlin para aliviar dores do Java, quais objetivos guiaram o projeto e onde a linguagem roda hoje, com datas conferidas. |
Fase 1: Primeiros passos (6 aulas)
Pergunta central: Como instalar o ambiente, escrever os primeiros programas e usar tipos, funções, coleções e exceções, sem saber nada de Kotlin?
| Aula | Pergunta que a aula responde |
|---|---|
| Ambiente e primeiro programa | Instale o JDK, o IntelliJ e o Gradle, crie um projeto Kotlin como as equipes criam e rode o primeiro programa pela IDE e pelo terminal. |
| Controle de fluxo: if, when e laços | Como o Kotlin escolhe e repete: if e when como expressões, ranges com .. e until, for, while, break, continue e labels, com saídas reais. |
| Funções: parâmetros, retorno e argumentos nomeados | Como declarar funções em Kotlin com fun, parâmetros, tipo de retorno, Unit, valores padrão e argumentos nomeados, com saídas reais de cada trecho. |
| Null safety e tipos nuláveis | Como String? muda o contrato de uma função, por que !! esconde uma decisão de modelagem e como tratar valores vindos do Java? |
| Coleções e lambdas | Listas, conjuntos e mapas no Kotlin, lambdas com it e as operações do dia a dia, como map, filter e groupBy, com saídas reais do Kotlin 2.4.21. |
| Exceções e primeiro teste automatizado | Veja como uma exceção interrompe o fluxo, como tratá-la com try, catch e finally e como escrever o primeiro teste com kotlin.test. |
Fase 2: POO em Kotlin (6 aulas)
Pergunta central: Como modelar o domínio com classes, encapsulamento, herança, interfaces, data class e sealed, partindo do zero em orientação a objetos?
| Aula | Pergunta que a aula responde |
|---|---|
| Classes, objetos e propriedades | Classes como moldes e objetos como instâncias, com construtor primário, propriedades, init e identidade em Kotlin, com saídas medidas no JDK 25 e no JDK 21. |
| Encapsulamento, visibilidade, object e companion | Aprenda a esconder o estado de uma classe atrás de ações que protegem as regras, com visibilidade, object, companion object e object expression. |
| Herança e polimorfismo | Aprenda a reaproveitar e especializar classes com open, override, super e abstract, e a tratar objetos de tipos diferentes da mesma forma em Kotlin. |
| Interfaces, delegação e classes open | Como compor comportamento com by em vez de herança, e por que classes são final por padrão? |
| data class, enum e value class | Quando usar data class para valores, o que ela gera por baixo, e como value class evita confundir UserId com OrderId sem custo extra? |
| sealed e when exaustivo | Como modelar estados de um processo para que o compilador avise quando um caso novo não foi tratado? |
Fase 3: Kotlin idiomático (8 aulas)
Pergunta central: Como combinar genéricos, funções de extensão, coleções e tratamento de erros para escrever Kotlin que aproveita o compilador?
| Aula | Pergunta que a aula responde |
|---|---|
| Genéricos básicos: classes e funções para vários tipos | Como escrever classes e funções genéricas em Kotlin com T, deixar o compilador deduzir o tipo, limitar T com Comparable e lidar com null, com saídas reais. |
| Funções de escopo e funções de extensão | Qual função de escopo usar em cada caso, e por que uma função de extensão não substitui um método virtual? |
| Coleções imutáveis e Sequence | Qual a diferença entre List e Sequence em uma cadeia de map e filter, e quando a alocação de coleções intermediárias importa? |
| Tratamento funcional de erros | Quando Result e runCatching ajudam, quando Either do Arrow vale o custo, e quando uma exceção continua sendo a escolha certa? |
| Variância e genéricos reified | Por que List<out T> aceita subtipos, e como reified permite acessar o tipo em tempo de execução? |
| Funções inline e custo de lambdas | Quando uma função inline elimina a alocação do lambda, e quando essa otimização não faz diferença? |
| Propriedades delegadas, operadores e destructuring | Delegates com by (lazy, observable, vetoable e um próprio), operadores com operator fun e destructuring com componentN: o que cada um resolve e onde engana. |
| Expressões e interop com Java | Como if, when e try produzem valores, e como expor uma API Kotlin de forma amigável para quem ainda chama pelo Java? |
Fase 4: Concorrência e coroutines (8 aulas)
Pergunta central: Como escrever código assíncrono que cancela, falha e termina de forma previsível, sem deixar tarefas órfãs?
| Aula | Pergunta que a aula responde |
|---|---|
| Threads e concorrência na JVM | Threads, condição de corrida, synchronized, volatile, pools com Future e deadlock na JVM, com código rodado em Kotlin e as saídas que apareceram de verdade. |
| suspend e a máquina de estados | O que o compilador gera a partir de uma função suspend, e por que ela não cria thread por si só? |
| CoroutineScope e concorrência estruturada | Quando coroutineScope falha tudo e supervisorScope isola falhas, e quem é responsável por cada coroutine criada? |
| Dispatchers e bloqueio | Por que bloquear Dispatchers.Default trava o serviço, e como limitedParallelism controla a concorrência? |
| Cancelamento cooperativo | Por que um catch genérico quebra o cancelamento, e como ensureActive e NonCancellable tratam a limpeza? |
| Flow, StateFlow e Channel | Quando um Flow frio basta, quando um fluxo quente é necessário, e quando um Channel coordena produtor e consumidor? |
| Estado compartilhado: Mutex e atomics | Proteja um contador compartilhado entre coroutines com Mutex, atomics, confinamento ou canais, e veja por que suspender não basta para evitar o erro. |
| Virtual threads e testes com runTest | Quando virtual threads do Java 21 são a escolha certa, e como testar coroutines com tempo virtual sem Thread.sleep? |
Fase 5: A JVM por baixo (5 aulas)
Pergunta central: O que o compilador gera e o que a JVM faz com esse código, para diagnosticar latência e memória?
| Aula | Pergunta que a aula responde |
|---|---|
| Bytecode e decompilação | O que vira objeto, método estático ou classe auxiliar ao compilar Kotlin, e como confirmar isso com javap? |
| Boxing e alocações escondidas | Por que Int vira Integer em coleções genéricas e em tipos nuláveis, e onde isso aparece em laços quentes? |
| Heap, GC e pausas | Como G1, ZGC e Shenandoah diferem em pausa e em custo de CPU, e como ler os logs de GC? |
| JIT e análise de escape | Quando o C2 remove uma alocação, e como escrever código que facilita essa otimização? |
| Profiling com JFR e startup | Como o JFR mostra alocação e pausas em produção, e quais as trocas entre CDS, AOT e GraalVM Native Image? |
Fase 6: APIs com Spring Boot (11 aulas)
Pergunta central: Como construir APIs com contratos claros, erros previsíveis, segurança e integração com outros serviços?
| Aula | Pergunta que a aula responde |
|---|---|
| HTTP e REST | Pedido e resposta HTTP, verbos, idempotência, códigos de status e desenho de recursos, com um servidor de pedidos em Kotlin rodado no JDK 21 e no 25. |
| Gradle Kotlin DSL e multi-módulo | Como organizar módulos e um catálogo de versões para que o build continue claro com o crescimento do projeto? |
| Injeção de dependência e IoC no Spring | Como o Spring monta os objetos de um serviço por injeção no construtor, com beans, @Primary, @Qualifier e dependência circular, em saídas reais. |
| Starters e configuração por ambiente | Como os starters do Spring Boot montam um serviço, e como application.yml, profiles e @ConfigurationProperties separam configuração de código? |
| Spring Boot com controllers suspend | Como validar entrada com Bean Validation e como controllers suspend se comportam no Spring MVC? |
| Serialização com kotlinx.serialization e Jackson | Quais as diferenças de comportamento com nulos e campos ausentes entre as duas bibliotecas? |
| Tratamento de erros com ProblemDetail | Como mapear exceções de domínio para status HTTP sem vazar detalhes internos? |
| Segurança com JWT e OpenAPI | Como validar claims em um resource server e documentar a API com springdoc sem expor o que não deveria? |
| Design avançado de API: paginação, versionamento e idempotência | Paginação por deslocamento e por cursor, versões de API, Idempotency-Key e ETag em Spring Boot, com requisições reais e as falhas que cada escolha esconde. |
| Clientes HTTP com RestClient e WebClient | Chame outro serviço com RestClient e WebClient, trate erros de status, faça o timeout disparar de verdade e teste o cliente sem rede, com saídas reais. |
| Mensageria com Kafka | Por que pôr um broker entre dois sistemas, como o Kafka ordena e entrega mensagens, e um produtor e um consumidor em Kotlin com reentrega medida. |
Fase 7: Persistência (8 aulas)
Pergunta central: Como acessar o banco com SQL, JPA, pool dimensionado, sem N+1 e com o schema versionado?
| Aula | Pergunta que a aula responde |
|---|---|
| SQL e modelagem relacional essencial | Tabelas, chaves, restrições, JOIN, agregação e normalização com clientes e pedidos, rodados no PostgreSQL 18.6 antes de qualquer ORM. |
| JPA básico: entidades, repositórios e @Transactional | Como mapear classes Kotlin para tabelas com JPA e Spring Data, e por que o rollback depende da classe da exceção, com saídas medidas em PostgreSQL. |
| JPA, Hibernate e N+1 | Quando o carregamento lazy causa N+1, e como fetch join e @EntityGraph resolvem o problema? |
| data class como entidade | Por que equals, hashCode e toString gerados podem disparar carregamento lazy ou recursão em relacionamentos? |
| Migrations com Liquibase | Como versionar o schema com changesets, por que um changeset já aplicado nunca deve ser editado e como o contexts separa dados de teste? |
| HikariCP e limites do banco | Como dimensionar o pool em relação ao número de threads e ao limite de conexões do Postgres? |
| Spring Data MongoDB: documentos e modelagem | Quando embutir um documento e quando referenciá-lo, e como MongoTemplate atende consultas que o repositório não cobre? |
| Índices e transações no MongoDB | Como usar explain para evitar varredura completa da coleção, e o que o replica set muda nas transações? |
Fase 8: Arquitetura, qualidade e operação (6 aulas)
Pergunta central: Como manter um serviço testável, observável e resiliente por anos, e como juntar tudo em um projeto completo?
| Aula | Pergunta que a aula responde |
|---|---|
| SOLID e padrões de projeto em Kotlin | Cinco princípios SOLID com código Kotlin que viola e depois corrige, padrões que a linguagem já resolve e o que sobra das regras de Object Calisthenics. |
| Arquitetura hexagonal com ArchUnit | Como manter o domínio sem dependência de framework e como impedir que a regra seja quebrada no futuro? |
| Testes com Kotest, MockK e Testcontainers | Quando usar testes de propriedade, por que MockK lida melhor com Kotlin do que Mockito, e como subir Postgres e MongoDB reais nos testes, além de testar o Azure Service Bus sem depender de um ambiente compartilhado? |
| Observabilidade com Micrometer e OpenTelemetry | Como expor métricas, propagar traces e manter logs estruturados em JSON no mesmo serviço? |
| Resiliência e empacotamento | Como aplicar circuit breaker e retry com jitter, e como gerar uma imagem Docker enxuta com camadas e jlink? |
| Projeto final: uma API completa de ponta a ponta | Uma API de pedidos com hexagonal, JWT, paginação, ProblemDetail, Liquibase, Micrometer e Docker, testada com Testcontainers e chamada com curl. |
Como as peças se encaixam
A ordem segue a dependência entre os temas. Os primeiros passos e a POO vêm antes do Kotlin idiomático porque map, filter e sealed só fazem sentido para quem já domina classes e coleções. As coroutines vêm antes das APIs porque um handler Spring que usa coroutines sem entender cancelamento produz vazamentos difíceis de rastrear, e a JVM por baixo vem depois delas porque o leitor precisa escrever código assíncrono antes de olhar bytecode, coletor de lixo e JIT. Persistência vem depois das APIs, e a fase 8 fecha com arquitetura, testes, observabilidade e um projeto que junta todas as peças.
Navegação e notas relacionadas no vault
- Currículo e referências: Curriculo de um Desenvolvedor Kotlin
- Trilha equivalente em Go, para comparação: Trilha de Go, Curriculo de um Desenvolvedor Go
- Conceitos de concorrência em Go, úteis para comparar com a fase 4: Channels, Context