MyFamilyTracker
Real-time family location sharing — Firebase Realtime DB for sub-second propagation, WorkManager + ForegroundService for OS-compliant background collection, geofencing via Google Maps API.
Discover how version catalogs eliminate version drift, streamline dependency updates, and keep 22+ Android projects consistent across a solo developer workflow.
On this page
I spent weeks chasing a missing security patch only to realize my
build.gradleWhen you manage dozens of modules from a Bangkok‑based solo shop, inconsistent library versions are inevitable. You end up with
implementation "androidx.appcompat:appcompat:1.6.1"1.5.0| Approach | Maintenance Effort | Version Consistency | Onboarding New Developers | Risk of Out‑of‑Date Libraries |
|---|---|---|---|---|
| Manual code | High – each module edited individually | Low – easy to diverge | Slow – each file must be inspected | High – human error |
| Version Catalog ( code | Low – single file updates | High – one source of truth | Fast – catalog defines all versions | Minimal – automated resolution |
A catalog turns dependency management from a scattered spreadsheet into a declarative TOML file. It also integrates with Gradle’s dependency resolution engine, so you get transitive version alignment for free.
Create
gradle/libs.versions.toml[versions]
# Core AndroidX libraries
appcompat = "1.7.0"
material = "1.12.0"
constraintlayout = "2.2.0"
# Jetpack Compose
compose-bom = "2024.06.01"
compose-ui = "1.7.0"
compose-material = "1.2.0"
compose-tooling = "1.7.0"
# Testing
junit = "4.13.2"
androidx-junit = "1.2.1"
espresso-core = "3.6.1"
# DI
hilt = "2.51"
# Networking
retrofit = "2.11.0"
okhttp = "4.12.0"
# Utilities
kotlinx-coroutines = "1.8.0"
kotlinx-serialization = "1.7.0"
[libraries]
# AndroidX
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
androidx-material = { group = "com.google.android.material", name = "material", version.ref = "material" }
androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
# Compose (BOM managed)
compose-ui = { group = "androidx.compose.ui", name = "ui", version.ref = "compose-ui" }
compose-material = { group = "androidx.compose.material", name = "material", version.ref = "compose-material" }
compose-tooling = { group = "androidx.compose.ui", name = "ui-tooling", version.ref = "compose-tooling" }
# Testing
junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-junit = { group = "androidx.junit", name = "junit", version.ref = "androidx-junit" }
espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso-core" }
# DI
hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" }
hilt-compiler = { group = "com.google.dagger", name = "hilt-compiler", version.ref = "hilt" }
# Networking
retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }
retrofit-serialization = { group = "com.squareup.retrofit2", name = "converter-kotlin-serialization", version.ref = "retrofit" }
okhttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp" }
# Utilities
kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "kotlinx-coroutines" }
kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinx-serialization" }
[plugins]
android = { id = "com.android.application", version.ref = "android" }
kotlin = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }[!TIP] Start with a minimal set – define only the libraries you currently use. Add new entries as your project grows; the catalog scales without extra overhead.
[!NOTE] Version references (
) keep the file readable. You can update a single line and every module that uses the alias picks up the new version automatically.codeversion.ref
[!WARNING] Avoid circular references – do not let a library version depend on another alias that itself references the first. Gradle will throw a resolution error.
[!IMPORTANT] Publish your catalog – if you maintain multiple apps, store the
in a shared repository (e.g., a Git submodule) and apply it viacodelibs.versions.tomlusingcodesettings.gradle.kts. This ensures every project inherits the same version matrix.codedependencyResolutionManagement
In each module's
build.gradle.ktsplugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("com.google.dagger.hilt.android")
id("org.jetbrains.kotlin.plugin.serialization")
}
android {
namespace = "com.sudarshan.app"
compileSdk = 34
defaultConfig {
applicationId = "com.sudarshan.app"
minSdk = 21
targetSdk = 34
versionCode = 1
versionName = "1.0.0"
}
buildTypes {
release {
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
}
buildFeatures {
compose = true
}
}
dependencies {
// Core AndroidX
implementation(libs.androidx.appcompat)
implementation(libs.androidx.material)
implementation(libs.androidx.constraintlayout)
// Compose (BOM is managed via libs.versions.toml)
implementation(libs.compose.ui)
implementation(libs.compose.material)
implementation(libs.compose.tooling)
// DI
implementation(libs.hilt.android)
kapt(libs.hilt.compiler)
// Networking
implementation(libs.retrofit)
implementation(libs.retrofit.serialization)
implementation(libs.okhttp)
// Utilities
implementation(libs.kotlinx.coroutines.android)
implementation(libs.kotlinx.serialization.json)
// Testing
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.espresso.core)
}[!IMPORTANT] Keep
in sync – the catalog entry forcodekaptmust match thecodehilt-compilerversion. If you update the DI version, add a correspondingcodehiltentry.codehilt-compiler
[!TIP] Leverage
object – Gradle automatically creates acodelibsobject in Kotlin DSL projects. You can also usecodelibsfor optional dependencies.codelibs.findLibrary("junit").get()
[!NOTE] BOM handling – the Compose BOM is declared as a separate version (
). You can optionally import it withcodecompose-bomto centralize version constraints for all Compose libraries. This pattern reduces the risk of version mismatches across UI components.codeimplementation(platform(libs.compose.bom))
| Practice | Why It Matters | How to Implement |
|---|---|---|
| Single source of truth | Prevents divergent versions across modules | Store code |
| Alias naming consistency | Makes search and refactoring easy | Use code code code |
| Separate version and library blocks | Improves readability | Keep code code |
| Version alignment with plugins | Avoids plugin‑library mismatches | Add plugin versions under code code |
| Regular catalog audits | Keeps dependencies secure and up‑to‑date | Run code |
[!WARNING] Do not mix
andcodeimplementationindiscriminately – usingcodeapiexposes transitive dependencies to consumers of your library, which can cause unexpected version conflicts in multi‑module projects.codeapi
[!TIP] Use
incodedependencyResolutionManagementto enforce the catalog globally:codesettings.gradle.kts
dependencyResolutionManagement {
versionCatalogs {
create("libs") {
from(files("../gradle/libs.versions.toml"))
}
}
}[!IMPORTANT] Version catalogs are not a substitute for testing – always run the full test suite after updating a library version. A catalog only guarantees version alignment; it does not guarantee runtime compatibility.
libs.versions.tomlbuild.gradle.ktsdependencyResolutionManagementSudarshan Chaudhari
AI Systems Builder / Product Engineer
Bangkok, Thailand
Solo Android developer with 13+ years in QA, building Android apps, AI automation systems, and developer tools at SudarshanTechLabs.
Related Posts
Related Apps
Real-time family location sharing — Firebase Realtime DB for sub-second propagation, WorkManager + ForegroundService for OS-compliant background collection, geofencing via Google Maps API.
Building something? Available for Android dev and QA consulting.
Work with meComments — powered by Giscus
Real-time family location sharing — Firebase Realtime DB for sub-second propagation, WorkManager + ForegroundService for OS-compliant background collection, geofencing via Google Maps API.
ReadPrivate dream journal — structured entry capture, pattern tagging, and optional Claude-powered insight generation. All data stays on-device by default.
ReadWorkout tracker — exercise logging with set/rep/weight history, goal progression, and local Room DB persistence. No account, no cloud sync required.
Read