
I started Android development writing XML by hand and debugging RecyclerView crashes at midnight. Here's what changed, what I learned, and why modern Android is genuinely exciting now.
My first Android app was a notes app. Not because notes apps are interesting — they're not — but because every tutorial in 2021 started with a notes app. I followed along, wrote XML layouts I didn't fully understand, copied ViewHolder boilerplate from Stack Overflow, and shipped it to the Play Store with zero downloads and one crash reported by the only person who installed it: me.
Three years later I'm maintaining production Android code used by enterprise IT teams across multiple countries. The gap between those two points is what this post is about.
Learning Android the 'old way' taught me something valuable: a deep appreciation for constraints. XML layouts force you to think about view hierarchy, measure/layout passes, and why a nested LinearLayout inside a RelativeLayout inside a ConstraintLayout is a performance problem. Understanding the View system makes you a better Compose developer because you understand what Compose is abstracting away.
But it was genuinely painful. ListView, then RecyclerView with its mandatory ViewHolder pattern. Fragment transactions with back stack logic that felt like you needed a PhD in state machine theory to get right. The `Activity` lifecycle — a seven-state diagram that still trips developers with five years of experience. Every non-trivial feature was a negotiation with the framework.
I once spent eight hours debugging a Fragment not restoring its scroll position after a configuration change. The fix was four lines of code. The understanding cost me a week.
Before I encountered Jetpack Compose, Google's Architecture Components — ViewModel, LiveData, Room — already dramatically improved how I thought about Android code. ViewModel surviving configuration changes meant I could stop the lifecycle-driven state loss that plagued my early apps. LiveData made reactive UI updates sane. Room made database access feel like proper software engineering rather than an error-prone SQL string assembly exercise.
By the time I started a full-time Android role, I was writing MVVM apps with clean architecture — separate data, domain, and presentation layers. The code was maintainable. Tests were possible. I felt like I was doing real engineering.
When I first saw Jetpack Compose in a conference talk, my reaction was: this is how Android should have been built. The View system is an imperative tree — you hold a reference to the view and mutate it. Compose is a declarative description — you describe what the UI should look like given the current state, and the framework reconciles the changes. It's the React mental model applied to mobile, and it solves the same class of problems React solved for the web.
// View system: imperative mutation
binding.usernameTextView.text = user.name
binding.avatarImageView.visibility = if (user.hasAvatar) View.VISIBLE else View.GONE
binding.statusBadge.setBackgroundColor(if (user.isOnline) Color.GREEN else Color.GRAY)
// Compose: declarative description
@Composable
fun UserProfile(user: User) {
Row(verticalAlignment = Alignment.CenterVertically) {
if (user.hasAvatar) AsyncImage(model = user.avatarUrl)
Text(text = user.name)
StatusBadge(isOnline = user.isOnline)
}
}The difference isn't just syntactic. In the View system version, you have to remember to call that code whenever user changes. You have to manage the reference to binding. You have to think about whether the view has been inflated yet. In Compose, none of that exists. If `user` changes, the UI reflects it. Full stop.
Learn the View system anyway. Compose is built on top of it, and most production codebases are hybrid for years. Understanding `Modifier` requires knowing what layout passes do. Using `AndroidView` inside a composable requires knowing the View lifecycle.
State hoisting is the hardest concept to internalize. Compose encourages you to push state up and pass down only what the composable needs. Violating this makes composables hard to test and prone to subtle bugs. The rule: if two composables need the same state, hoist it to their lowest common ancestor.
Recomposition is not free. Compose is smart about skipping recomposition for composables whose inputs haven't changed, but you have to give it the opportunity. Unstable classes, lambda captures, and anonymous lambdas in frequently-recomposed scopes are common performance traps. Read the Compose performance guide before you have a production performance problem, not after.
Working on enterprise Android — MDM software, device management dashboards, policy enforcement UIs — is a different category of challenge than consumer apps. The users are power users. The data is dense. The failure modes have real business consequences. But Compose has made it possible to build and iterate faster than I thought the Android platform could support.
If you're starting Android development today, start with Compose. Not because the View system doesn't matter — it does — but because Compose represents where the platform is going, and the mental model it builds is the right one for modern mobile development. The XML era gave me the foundations. Compose gave me the velocity.
Then you’re in the right place. Get the best solution you’re looking for. Just reach out and let me know!