Stop Using Booleans for State in Android — You’re Creating Bugs You Can’t See
I used to think this was harmless. We’ve all been there setting up a couple of booleans to track UI state in our ViewModels:
isLoading = true
hasError = false
isDataLoaded = true
Simple, right?
Until it isn’t. Because the moment you introduce multiple flags to represent your UI’s state, you’ve quietly opened the door to impossible situations.
Think about your current codebase. What happens when:
isLoading = trueANDisDataLoaded = true?hasError = truebut the data is still showing on the screen?All flags are
false… so what state are we even in?
These aren’t just theoretical problems; they happen in real applications every day. And the worst part is that the compiler won’t save you. It trusts that you know what you are doing.
The Real Problem: You’re Modeling State Incorrectly
Booleans don’t represent state. They represent conditions.
Your UI is not a set of independent, overlapping conditions. It is a finite set of mutually exclusive states. When you model it incorrectly by juggling independent variables, your app becomes exponentially harder to reason about, harder to test, and infinitely easier to break.
The Fix: Model State as a Single Source of Truth
Instead of spinning plates and toggling flags, define your UI state explicitly. In Kotlin, this is exactly where sealed interfaces (or sealed classes) shine.
Here is what a clean, bulletproof state model looks like:
Kotlin
sealed interface UiState {
object Loading : UiState
data class Success(val data: List<Item>) : UiState
data class Error(val message: String) : UiState
}
With this approach, your UI can only ever be in one valid state at a time. No contradictions. No ambiguity. No guessing.
Why This Changes Everything
Making this switch isn’t just a stylistic preference; it structurally improves your code in three major ways:
The compiler becomes your safety net: When you use a
whenstatement, Kotlin physically forces you to handle every possible state. You can’t accidentally forget to handle an error screen.Impossible states become impossible: You physically cannot represent conflicting UI states (like loading and erroring simultaneously) anymore.
Your code becomes self-documenting: Anyone reading your codebase understands the entire UI flow instantly, just by looking at the sealed interface.
Where This Matters Most
You’ll feel the impact of this architectural shift immediately, especially in:
Complex screens pulling from multiple data sources.
Intricate error handling and retry flows.
Jetpack Compose UIs, where state dictates absolutely everything on the screen.
If your UI logic constantly feels fragile, boolean fatigue is usually the culprit.
The Subtle Leadership Lesson
This isn’t just about writing cleaner code. It’s about how we think as engineers.
Early in your career, you optimize for speed: “Just add another flag and move on.” But as systems grow, that mindset simply doesn’t scale. Senior engineers don’t just solve problems—they architect systems that prevent entire classes of problems from existing in the first place.
Transitioning from independent booleans to a unified state model is one of those pivotal shifts in engineering maturity.
A Simple Gut Check
Look at your current project. If your ViewModel has more than one boolean controlling the UI, you’re probably one edge case away from a bug.
Clean architecture isn’t about being overly complex or fancy. It’s about making the wrong thing hard to do. And modeling state correctly? That’s one of the highest-leverage changes you can make in an Android codebase.
