When I created my first Android project, Android Studio auto-filled three numbers I'd never seen before:
compileSdk = 35
minSdk = 24
targetSdk = 35
I left them exactly as they were. Didn't touch them. Didn't read them. Moved straight to writing code.
That worked fine — until the day I added a new AndroidX library and got hit with an error I still remember clearly:
Dependency 'androidx.core:core:1.17.0' requires compileSdk 36 or later.
I hadn't written a single bad line of code. The project was clean. And yet it refused to build.
That was the moment I realized I had absolutely no idea what those three numbers meant, or why they existed. If you're in the same boat — this is for you.
Three Numbers, One Job Each
Let me break these down in the order that actually makes sense — not alphabetical, not the order they appear in the file, but the order that makes them click.
minSdk — The Minimum Bar to Install Your App
This is the oldest version of Android that can run your app.
minSdk = 24 // Android 7.0 (Nougat)
If a phone is running Android 6.0 (API 23) and your minSdk is 24, that phone simply won't be able to install your app. The Play Store will hide your app from them entirely. They'll never even see it.
The lower you set minSdk, the more devices you support. But here's the catch — the lower you go, the more old Android behaviors you have to handle in your code. Features that exist on API 30 might not exist on API 21. You'd have to write fallback code for every one of them.
In 2026, minSdk = 24 is the sweet spot for most new apps. It covers roughly 98% of active Android devices and lets you use pretty much every modern API without worrying about ancient phones.
The one-liner: minSdk decides who can install your app.
compileSdk — What APIs You Can Actually Use
This is the Android SDK version Gradle uses to compile your code. It's not about which phones can run your app. It's about which Android APIs are available to you while you're writing code.
compileSdk = 36 // Android 16
If you set compileSdk = 34 but try to call an API that was introduced in API 35, you'll get a compile error. Your IDE won't even recognize the method. Because as far as your build system is concerned, that API doesn't exist yet — you're compiling against an older SDK.
This is also what that error I got was about. The core:1.17.0 library internally uses APIs that only exist in SDK 36+. It said: "if you want to use me, you need to be compiling against at least SDK 36." Fair enough.
The rule of thumb: keep compileSdk at the latest stable Android version. There's no downside. It doesn't affect which devices run your app — it just unlocks newer APIs for you to call.
The one-liner: compileSdk decides what you can write in your code.
targetSdk — The Version You're "Promising" to Support
This one is the trickiest, but here's the mental model that helped me:
targetSdk is your app saying to the Android OS: "I know about this version of Android. I've tested against it. Please apply its rules to me."
targetSdk = 35 // Android 15
Android introduces new behavior changes with every version — things like stricter background restrictions, new permission flows, different notification handling. But to avoid breaking millions of existing apps overnight, Android only applies those new behaviors to apps that explicitly declare they're ready for them, via targetSdk.
If you set targetSdk = 33, Android will still run your app on a brand-new Pixel — but it'll treat it like an Android 13 app, applying Android 13 rules. That's a safety net, not a feature.
The one-liner: targetSdk decides which Android rules apply to your app at runtime.
The Three-Line Summary You'll Actually Remember
android {
compileSdk = 36 // What APIs can I write in my code?
minSdk = 24 // Which phones can install my app?
targetSdk = 35 // Which Android behavior rules apply to me?
}
minSdk ≤ targetSdk ≤ compileSdk — that's always the order. They don't go the other way.
Now Let's Talk About Your App's Secret Evil Twin
Here's something that surprised me when I first found it.
Your app doesn't have one version. It has two. At all times. Living in your project simultaneously.
One is the app you run during development. One is the app you ship to users. And they are not the same thing.
This is what buildTypes is about.
buildTypes {
debug {
// This is the version running on YOUR phone right now
}
release {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
Debug: Your Development Companion
Every time you hit the Run button, Android Studio builds the debug version of your app.
The debug build is built for you, not for users. It's:
- Debuggable — you can attach the debugger, set breakpoints, inspect variables while the app runs
- Not optimized — builds fast so you're not waiting forever between changes
- Signed with a debug key automatically — you don't need to set up signing to install it on your own device
- Bigger — no code shrinking, no obfuscation, all the extra debug info is kept in
You'll never publish a debug APK. You shouldn't even want to — it has debug flags enabled that you don't want visible in production, and it's larger than it needs to be.
Release: What Your Users Get
The release build is what you ship to the Play Store. It's built for users, not for you.
release {
isMinifyEnabled = true // shrinks and obfuscates your code with R8
isShrinkResources = true // removes unused resources from the APK
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
The release build is:
- Optimized — R8 runs and shrinks your code down significantly
- Obfuscated — class and method names get renamed to short gibberish, making it harder to reverse-engineer
- Smaller — unused code and resources get stripped out
- Not debuggable — you can't attach the Android debugger to it (which is intentional)
- Signed with your real keystore — required to publish to the Play Store
Here's a thing that trips up a lot of beginners: your app can behave differently in release vs debug.
The lesson: always test a release build before you ship it. Don't assume that because it worked in debug, it'll work in release too.
The Cheat Sheet
| Config | Question it answers | Value to use in 2026 |
|---|---|---|
compileSdk |
What APIs can I use in code? | 36 |
targetSdk |
Which Android rules apply? | 35 |
minSdk |
Who can install my app? |
24 (covers ~98% of devices) |
debug build |
Who's it for? | You, during development |
release build |
Who's it for? | Your users |
Top comments (0)