Se você trabalha com Kotlin — seja no Android, backend com Spring Boot ou Ktor — já deve ter sofrido com gerenciamento de dependências no Gradle. Versões espalhadas por múltiplos build.gradle.kts, conflitos difíceis de rastrear e atualizações manuais em dezenas de módulos.
O Gradle Version Catalog resolve tudo isso com um único arquivo TOML centralizado. Neste guia prático, você vai aprender a configurar, migrar e dominar essa feature que já virou padrão em projetos Kotlin modernos.
O que é o Gradle Version Catalog?
O Version Catalog é um recurso nativo do Gradle (disponível desde a versão 7.0 e estável desde a 7.4.1) que centraliza todas as dependências, plugins e versões do seu projeto em um arquivo chamado libs.versions.toml.
Em vez de espalhar versões assim:
// módulo app/build.gradle.kts
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.1")
implementation("io.ktor:ktor-server-core:3.1.1")
}
// módulo shared/build.gradle.kts
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.1") // duplicado!
}
Você declara tudo num lugar só e referencia com type-safe accessors:
// módulo app/build.gradle.kts
dependencies {
implementation(libs.kotlinx.coroutines.core)
implementation(libs.ktor.server.core)
}
Se você já leu nosso tutorial de Gradle com Kotlin, o Version Catalog é a evolução natural para projetos com múltiplos módulos.
Criando o arquivo libs.versions.toml
O arquivo fica em gradle/libs.versions.toml na raiz do projeto. O Gradle o detecta automaticamente.
# gradle/libs.versions.toml
[versions]
kotlin = "2.3.20"
coroutines = "1.10.1"
ktor = "3.1.1"
exposed = "1.0.0"
logback = "1.5.15"
junit = "5.11.4"
mockk = "1.13.16"
[libraries]
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }
kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutines" }
ktor-server-core = { module = "io.ktor:ktor-server-core", version.ref = "ktor" }
ktor-server-netty = { module = "io.ktor:ktor-server-netty", version.ref = "ktor" }
ktor-server-content-negotiation = { module = "io.ktor:ktor-server-content-negotiation", version.ref = "ktor" }
ktor-serialization-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" }
exposed-core = { module = "org.jetbrains.exposed:exposed-core", version.ref = "exposed" }
exposed-dao = { module = "org.jetbrains.exposed:exposed-dao", version.ref = "exposed" }
exposed-jdbc = { module = "org.jetbrains.exposed:exposed-jdbc", version.ref = "exposed" }
logback-classic = { module = "ch.qos.logback:logback-classic", version.ref = "logback" }
junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit" }
mockk = { module = "io.mockk:mockk", version.ref = "mockk" }
[bundles]
ktor-server = ["ktor-server-core", "ktor-server-netty", "ktor-server-content-negotiation", "ktor-serialization-json"]
exposed = ["exposed-core", "exposed-dao", "exposed-jdbc"]
testing = ["junit-jupiter", "mockk", "kotlinx-coroutines-test"]
[plugins]
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
ktor = { id = "io.ktor.plugin", version.ref = "ktor" }
O arquivo tem quatro seções:
| Seção | Propósito |
|---|---|
[versions] | Declara versões reutilizáveis |
[libraries] | Define dependências com referência a versões |
[bundles] | Agrupa dependências relacionadas |
[plugins] | Declara plugins do Gradle |
Usando no build.gradle.kts
Depois de criar o TOML, use os accessors gerados automaticamente:
// build.gradle.kts (raiz)
plugins {
alias(libs.plugins.kotlin.jvm) apply false
alias(libs.plugins.ktor) apply false
}
// app/build.gradle.kts
plugins {
alias(libs.plugins.kotlin.jvm)
alias(libs.plugins.ktor)
alias(libs.plugins.kotlin.serialization)
}
dependencies {
// Usando bundle — adiciona todas as deps do Ktor de uma vez
implementation(libs.bundles.ktor.server)
// Usando bundle do Exposed
implementation(libs.bundles.exposed)
// Dependência individual
implementation(libs.logback.classic)
// Bundle de testes
testImplementation(libs.bundles.testing)
}
Perceba como os bundles simplificam o código. Em vez de listar 4 dependências do Ktor, uma linha resolve. Isso é especialmente útil em projetos multi-módulo — algo que exploramos no artigo sobre monólito modular com Kotlin.
Exemplo prático: projeto Android com Compose
Para projetos Android com Jetpack Compose, o Version Catalog brilha ainda mais:
# gradle/libs.versions.toml
[versions]
kotlin = "2.3.20"
agp = "8.8.2"
compose-bom = "2025.03.00"
compose-compiler = "2.0.0"
lifecycle = "2.8.7"
navigation = "2.8.5"
hilt = "2.53.1"
room = "2.6.1"
coroutines = "1.10.1"
[libraries]
compose-bom = { module = "androidx.compose:compose-bom", version.ref = "compose-bom" }
compose-ui = { module = "androidx.compose.ui:ui" }
compose-material3 = { module = "androidx.compose.material3:material3" }
compose-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview" }
compose-tooling = { module = "androidx.compose.ui:ui-tooling" }
lifecycle-runtime = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "lifecycle" }
lifecycle-viewmodel = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycle" }
navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigation" }
room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" }
room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" }
[bundles]
compose = ["compose-ui", "compose-material3", "compose-tooling-preview"]
lifecycle = ["lifecycle-runtime", "lifecycle-viewmodel"]
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
E no build.gradle.kts do módulo:
dependencies {
implementation(platform(libs.compose.bom))
implementation(libs.bundles.compose)
implementation(libs.bundles.lifecycle)
implementation(libs.navigation.compose)
implementation(libs.room.runtime)
implementation(libs.room.ktx)
ksp(libs.room.compiler)
debugImplementation(libs.compose.tooling)
}
Se você está começando com Compose, confira nosso tutorial básico de Jetpack Compose e o guia de layouts no Compose.
Bundles: agrupando dependências
Os bundles são uma das features mais úteis. Eles agrupam dependências que sempre andam juntas:
[bundles]
# Tudo que precisa para um server Ktor com JSON
ktor-server = [
"ktor-server-core",
"ktor-server-netty",
"ktor-server-content-negotiation",
"ktor-serialization-json"
]
# Stack de testes completa
testing = [
"junit-jupiter",
"mockk",
"kotlinx-coroutines-test"
]
Se você trabalha com testes em Kotlin usando JUnit5 e MockK, criar um bundle de testes evita repetir as mesmas 3-5 dependências em cada módulo.
Migrando um projeto existente
A migração é simples e incremental — você não precisa converter tudo de uma vez:
Passo 1: Crie o arquivo TOML
mkdir -p gradle
touch gradle/libs.versions.toml
Passo 2: Extraia versões existentes
Procure todas as versões hardcoded nos seus build.gradle.kts e mova para o TOML:
// ANTES — build.gradle.kts
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.1")
}
// DEPOIS — build.gradle.kts
dependencies {
implementation(libs.kotlinx.coroutines.core)
}
Passo 3: Sync e valide
./gradlew build
O Gradle gera os accessors automaticamente. Se usar IntelliJ IDEA ou Android Studio, o autocomplete funciona imediatamente.
Passo 4: Converta módulo por módulo
Não precisa migrar todos os módulos de uma vez. O Gradle permite misturar strings com accessors durante a transição.
Boas práticas
Depois de ter configurado diversos projetos Kotlin com Version Catalog, estas são as práticas que mais fazem diferença:
Use kebab-case nos nomes —
ktor-server-coreviralibs.ktor.server.core. O Gradle converte automaticamente.Agrupe por ecossistema — organize as libraries por família (Ktor, Exposed, Compose, etc.) para facilitar a leitura.
Crie bundles para stacks recorrentes — se seus módulos sempre usam as mesmas 4 dependências juntas, faça um bundle.
Não abuse das versões compartilhadas — nem toda dependência precisa de
version.ref. Se só uma library usa aquela versão, declare inline:version = "1.0.0".Mantenha o TOML atualizado — ferramentas como Renovate e Dependabot já suportam
libs.versions.toml.Documente com comentários — o formato TOML aceita
#para comentários. Use para explicar por que uma versão está pinada.
Para mais dicas de organização de projetos, confira nosso guia de Gradle com Kotlin DSL.
Version Catalog vs buildSrc vs Convention Plugins
É comum a dúvida: quando usar cada abordagem?
| Abordagem | Quando usar |
|---|---|
| Version Catalog | Centralizar versões e dependências |
| buildSrc | Lógica de build compartilhada simples |
| Convention Plugins | Configurações de build complexas e reutilizáveis |
Na prática, a maioria dos projetos modernos combina Version Catalog + Convention Plugins. O catalog cuida das versões, e os convention plugins cuidam da configuração dos módulos.
Se quiser entender melhor o build system do Kotlin, temos um guia completo de Gradle e um artigo sobre o Amper, o novo build tool da JetBrains.
Perguntas Frequentes
O Version Catalog funciona com Kotlin Multiplatform?
Sim, funciona perfeitamente com KMP. Você pode declarar dependências para cada source set (commonMain, androidMain, iosMain) e referenciá-las com os mesmos accessors type-safe.
Preciso de alguma versão mínima do Gradle?
O recurso ficou estável no Gradle 7.4.1. Se você usa Kotlin 2.x, já está em uma versão compatível. Projetos com Kotlin 2.3.20 ou Kotlin 2.4.0 usam Gradle 8.x, que tem suporte completo.
Posso usar Version Catalog com projetos Spring Boot?
Com certeza. O Spring Boot com Kotlin funciona normalmente com Version Catalog. Basta declarar o plugin do Spring e as dependências no TOML.
O autocomplete funciona no IntelliJ?
Sim. Tanto o IntelliJ IDEA quanto o Android Studio oferecem autocomplete completo para os accessors gerados. Confira nosso artigo sobre as melhores IDEs para Kotlin.
Como atualizar dependências automaticamente?
Use Renovate ou Dependabot — ambos já parseiam libs.versions.toml. Combine com CI/CD para validar cada atualização com testes automáticos.
Conclusão
O Gradle Version Catalog não é mais uma novidade — é o padrão em projetos Kotlin modernos. Se você ainda gerencia dependências com strings hardcoded, está desperdiçando tempo e acumulando risco de conflitos.
A migração é simples, incremental e os benefícios aparecem no primeiro dia: menos duplicação, autocomplete, bundles que simplificam módulos e um único lugar para atualizar versões.
Se você está começando no ecossistema Kotlin, explore nosso guia completo e veja as tendências para 2026. E se está planejando a carreira, confira o roadmap para dev backend Kotlin — dominar o build system é diferencial em entrevistas. Para quem também trabalha com Go, vale comparar como o go.mod resolve o gerenciamento de dependências de forma nativa — cada ecossistema tem sua abordagem, e conhecer várias amplia sua visão como dev.