You’ll see build types early in any Android project. Here’s the short answer: build types control how your app is compiled and packaged for different moments—coding, testing, and shipping.
A simple way to think about it is… you wear different “outfits” for different occasions: sneakers for practice debug, formal shoes for a wedding release. Same person, different outfit, different rules.
Why this matters in one minute
- You can install debug and release builds side‑by‑side without clashing.
- You can point the app to different servers like staging vs production.
- You can hide dev‑only tools from real users.
- Your QA team can tell builds apart quickly.
We should avoid thinking that build types were just labels. In real projects, they’re guardrails that keep experiments out of production.
Prerequisites light - For Beginners who don't know what we are alking about
- You’ve built a basic Android app in Kotlin or Java.
- You can open and edit your
build.gradle.ktsfile.
Term check plain English:
- Build type: A named set of rules for compiling/packaging your app e.g.,
debug,release.- Application ID: The unique package name used on a device/store e.g.,
com.example.app. Two apps can live side‑by‑side if their IDs differ.BuildConfig: A generated class with constants you can read in code likeDEBUG,APPLICATION_ID.
If you’re a beginner, this is the part that unlocks a lot of “how do they do that?” moments when you see teams switch between environments. So let's get started.
The starting point Gradle snippet
Typically this how a starting build.gradle.kts file looks like.
android
namespace = "com.codetutor.varientdemo"
compileSdk = 36
defaultConfig
applicationId = "com.codetutor.varientdemo"
minSdk = 24
targetSdk = 36
versionCode = 1
versionName = "1.0"
buildTypes
release
isMinifyEnabled = false
proguardFiles
getDefaultProguardFile"proguard-android-optimize.txt",
"proguard-rules.pro"
buildFeatures
buildConfig = true
What this gives you
- A
releasebuild type defined. BuildConfiggeneration is on, so you can use constants in code.
What I’ve noticed is folks forget buildFeatures buildConfig = false and then wonder why BuildConfig isn’t available. On paper this looks simple, but… one missing line leads to cryptic compile errors.
Enable and use BuildConfig
Turn it on already shown above
buildFeatures
buildConfig = true
Read values in code
Text"APPLICATION_ID: BuildConfig.APPLICATION_ID"
Text"VERSION_NAME: BuildConfig.VERSION_NAME"
Text"DEBUG: BuildConfig.DEBUG"
Why this is useful: You can branch behavior e.g., show a debug banner and display environment info for quick checks.
Tell debug and release apart clearly
This is where most people get stuck… The app icons look the same, and you can’t tell which build is which on a device. Let’s fix that with suffixes and labels.
1 Add suffixes for debug
buildTypes
debug
applicationIdSuffix = ".debug"
versionNameSuffix = "-debug"
isMinifyEnabled = false
release
isMinifyEnabled = false
signingConfig = signingConfigs.getByName"debug"
proguardFiles
getDefaultProguardFile"proguard-android-optimize.txt",
"proguard-rules.pro"
- Debug app ID becomes:
com.codetutor.varientdemo.debug - Release app ID stays:
com.codetutor.varientdemo
2 Change the launcher label per build
Use manifestPlaceholders so the app name shows which build you opened.
buildTypes
debug
manifestPlaceholders["appLabel"] = "Varient Demo dbg"
applicationIdSuffix = ".debug"
versionNameSuffix = "-debug"
isMinifyEnabled = false
release
manifestPlaceholders["appLabel"] = "Varient Demo"
isMinifyEnabled = false
signingConfig = signingConfigs.getByName"debug"
Now you’ll see the difference on your phone right away.
Add build‑specific config like different APIs
Point debug to staging and release to production:
buildTypes
debug
manifestPlaceholders["appLabel"] = "Varient Demo dbg"
applicationIdSuffix = ".debug"
versionNameSuffix = "-debug"
buildConfigField"String", "BASE_URL", ""https://staging.api.example.com""
release
manifestPlaceholders["appLabel"] = "Varient Demo"
buildConfigField"String", "BASE_URL", ""https://api.example.com""
Read it in code:
KeyValue"BASE_URL", BuildConfig.BASE_URL
In real projects, this small switch avoids “why is my local build hitting production?” chaos.
Debug‑only features safe by default
You can guard tools and experiments behind BuildConfig.DEBUG so they never leak to real users.
@Composable
fun ConfigScreen
Surfacemodifier = Modifier.fillMaxSize
Column
// Show debug indicator only in debug builds
if BuildConfig.DEBUG
AssistChip
Text"Varients Demo", style = MaterialTheme.typography.headlineSmall
KeyValue"APPLICATION_ID", BuildConfig.APPLICATION_ID
KeyValue"VERSION_NAME", BuildConfig.VERSION_NAME ?: "—"
KeyValue"DEBUG flag", BuildConfig.DEBUG.toString
KeyValue"BASE_URL", BuildConfig.BASE_URL
// Debug-only tools
if BuildConfig.DEBUG
val activity = LocalContext.current as Activity
ButtononClick =
ActivityLeaker.leakactivity as MainActivity
Text"Trigger Activity Leak"
Why this helps: You keep power tools for developers without risking the production app.
Signing the quick mental model
- Debug builds: auto‑signed with a debug keystore so you can install and test right away.
- Release builds: must be signed with your release keystore before store upload.
For local testing only, you can temporarily reuse the debug signing for release:
release
signingConfig = signingConfigs.getByName"debug"
Important: For production, always use a dedicated, secure release keystore.
Common pitfalls and fixes
- Identical app names/icons → Use
manifestPlaceholders["appLabel"]to label debug builds. - Same applicationId → Add
applicationIdSuffix = ".debug"so both builds install together. - Leaky dev tools → Wrap them with
if BuildConfig.DEBUGchecks. - Wrong server calls → Set
buildConfigField"String", "BASE_URL", "..."per build type.
According to me, these four cover 80% of the “oops” moments for teams.
Takeaway
- Build types aren’t fluff; they’re practical switches for safer development.
- Start with clear labels, ID suffixes, and separate BASE_URLs.
- Keep debug‑only tools behind
BuildConfig.DEBUG.
Next step: When you’re ready, explore Product Flavors to handle multiple environments QA, UAT, Production or brand variants—that’s the more advanced layer on top of build types.