I'm not even exaggerating with that number. 700 lines. One file. One class. Every single thing my app did — API calls, click listeners, UI updates, data formatting — all crammed into MainActivity.kt like a suitcase someone sat on to close.
It worked. Until it didn't.
The day it fell apart was when I tried to add a simple line of Code. I ended up breaking the list, the error message, and somehow the back button. I spent four hours fixing something that should've taken ten minutes.
That's when I finally stopped avoiding the word I'd been scared of since I started: architecture.
Why I Ignored It For So Long
Honestly? The word itself put me off.
Architecture. It sounds like something senior engineers debate in long meetings. Not something a beginner building their first to-do app needs to worry about.
So I kept ignoring it. Kept piling more code into my Activity. Kept telling myself I'd "refactor later."
Later never came. The code just got worse.
If you're in that same place right now — I get it. But let me tell you what nobody told me: MVVM isn't complicated. It's actually just a way of answering one question —
Who is responsible for what?
That's it. Seriously.
The Analogy That Made It Click For Me
Forget code for a second. Think about a restaurant.
When you go out to eat, there are three people involved in getting food to your table:
- 🍽️ The waiter takes your order and brings your food. They don't cook anything. They don't decide what's on the menu. They just deal with you.
- 👨🍳 The chef does all the actual work. Takes the order from the waiter, figures out how to prepare it, sends it back out. Never comes to your table.
- 📦 The stockroom has all the ingredients. The chef goes there to get what they need. You never see it. The waiter definitely doesn't go back there.
Three people. Three jobs. Nobody steps on anyone else's toes.
MVVM is exactly this:
- Your View (Activity, Fragment, Composable) = the waiter. Shows things to the user. Reacts to what the user does. That's all.
- Your ViewModel = the chef. Holds the logic. Decides what data to use and when. Never touches the UI directly.
- Your Model (Repository, database, API) = the stockroom. Just holds and provides data. Doesn't care who's asking or why.
When I understood this, everything else fell into place.
What My Code Looked Like Before (Please Don't Judge Me)
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// calling the API straight from the Activity 💀
CoroutineScope(Dispatchers.IO).launch {
val users = RetrofitClient.api.getUsers()
withContext(Dispatchers.Main) {
adapter.submitList(users)
}
}
}
}
This looks innocent. It's not.
Rotate the phone → Activity gets destroyed → coroutine gets cancelled
→ the API gets called again from scratch. Every. Single. Rotation.
Also, try writing a test for this. You can't. The networking is baked directly into the UI layer. There's no way to test the logic without spinning up the whole Activity.
I didn't even know this was a problem until it bit me.
What It Looks Like After
Here's the same feature split across three layers the right way:
The Model — just fetches data, nothing else
class UserRepository(private val api: ApiService) {
suspend fun getUsers(): List<User> = api.getUsers()
}
The ViewModel — holds the logic and the state
class UserViewModel(private val repository: UserRepository) : ViewModel() {
private val _users = MutableStateFlow<List<User>>(emptyList())
val users: StateFlow<List<User>> = _users
init { loadUsers() }
private fun loadUsers() {
viewModelScope.launch {
_users.value = repository.getUsers()
}
}
}
The View — just watches and reacts
class MainActivity : AppCompatActivity() {
private val viewModel: UserViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
lifecycleScope.launch {
viewModel.users.collect { users ->
adapter.submitList(users)
}
}
}
}
Three files. Each one does one thing. The Activity has no idea how users are fetched. The ViewModel has no idea how they're displayed. And if I rotate the phone? The ViewModel is still alive — data doesn't reload, nothing breaks.
The One Thing I Wish Someone Had Told Me
The ViewModel isn't just an organizational trick. It has an actual superpower that nothing else gives you:
It survives configuration changes.
When you rotate your phone, Android destroys and recreates your Activity. This is just how Android works — it's not a bug. But if your data lives inside the Activity, it dies with it.
A ViewModel doesn't. It stays alive across rotations, theme changes, keyboard toggles — anything that would normally kill your Activity. When the Activity comes back, it reconnects to the same ViewModel that was already there, with all its data intact.
That alone is worth learning MVVM for.
The Honest Truth About Getting Started
You're not going to write perfect MVVM on your first try. I didn't. My first "MVVM" app still had logic leaking into the Activity in places. I used LiveData wrong. My Repository was doing things it shouldn't.
That's fine.
The goal isn't perfection. The goal is to stop putting everything in one place. Even a messy, imperfect separation of concerns is infinitely better than a 700-line Activity.
Start on your next project. Even a small one. Just ask yourself before writing anything: does this belong in the View, the ViewModel, or the Model?
That question alone will change how you write code.

Top comments (0)