The Multi-Module Build Configuration Problem
Every Android project that grows beyond a single module hits duplicated build configuration. Module after module declares the same compileSdk, Kotlin version, testing dependencies, and proguard rules. Developers copy a working build.gradle.kts, tweak module-specific parts, and move on. Within months, 15 modules have 15 slightly different build files with dependency version drift, inconsistent compile options, and cognitive overhead that makes build maintenance a dreaded chore.
Version Catalogs: Single Source of Truth
A Gradle version catalog (gradle/libs.versions.toml) declares all dependency coordinates in one TOML file. Modules reference them through generated type-safe accessors: instead of hardcoding group:artifact:version strings, you write libs.androidx.core.ktx. Versions are declared once, typos become compile-time errors, and IDE autocompletion makes dependencies discoverable. Bundles group commonly-used libraries (Compose UI + Material 3 + tooling) for one-line application. On a 15-module project, dependency version drift becomes impossible.
Convention Plugins: Extracting Shared Build Logic
Convention plugins solve everything version catalogs don't: compileSdk, minSdk, Kotlin compiler options, Compose configuration, test runner setup, proguard rules. Defined in a build-logic module as Kotlin classes implementing Plugin<Project>, each plugin configures one concern. Layer them: AndroidLibraryConventionPlugin handles SDK targets, ComposeConventionPlugin handles Compose compiler, HiltConventionPlugin handles DI setup. Feature modules compose only the plugins they need. Because the build-logic module is a regular Kotlin project, you get full IDE support and can write unit tests for build configuration.
The Result: 10-Line Feature Module Build Files
With both patterns in place, a new feature module's build.gradle.kts contains plugin applications and module-specific dependencies — roughly 10 lines instead of 80. Updating compileSdk changes one file instead of 15. Adding a Compose compiler option changes one file. New module creation cannot accidentally forget settings because decisions are encoded in convention plugins, not left to individual authors. The first time a Kotlin version bump changes one file instead of fifteen, the infrastructure pays for itself.
Incremental Migration Strategy
Adoption is incremental: Phase 1 creates the TOML catalog and migrates dependencies one module at a time. Phase 2 creates the build-logic module with one convention plugin, proving the infrastructure on a single module. Phase 3 expands convention plugins for Compose, Hilt, and testing. Phase 4 adds CI checks that reject direct compileSdk settings. The entire migration takes 2-4 weeks for a 15-module project without blocking feature work. Each phase is independently valuable and mergeable.



