The Problem

Using var in Kotlin data class constructors causes performance issues in Jetpack Compose apps.

Bad:

data class UserProfile(
    val id: String,
    val name: String,
    var isMarried: Boolean  // This breaks Compose optimization
)

Good:

data class UserProfile(
    val id: String,
    val name: String,
    val isMarried: Boolean  // Immutable = optimized
)

What Happens Under the Hood

Compose Stability Analysis

  • Compose compiler analyzes classes at build time
  • Classes with var properties = marked as “unstable”
  • Unstable classes = unnecessary recompositions
  • More recompositions = worse performance

The Impact

// With var: UserProfile is UNSTABLE
data class UserProfile(var isMarried: Boolean)
 
// Result: Every UI update triggers recomposition
// Even when UserProfile data didn't change

Real Performance Difference

Before (with var):

  • UserProfile: UNSTABLE
  • UI recomposes on every state change
  • Layout Inspector shows constant recomposition counts

After (with val):

  • UserProfile: STABLE
  • Compose skips unnecessary recompositions
  • Layout Inspector shows optimized rendering

How to Check Performance

Enable Compose compiler metrics in gradle.properties:

androidx.enableComposeCompilerMetrics=true
androidx.enableComposeCompilerReports=true

Check generated reports in app/build/compose_compiler/:

  • *-classes.txt → stability information
  • *-composables.txt → recomposition behavior

Why This Matters

Performance Impact

  • Unnecessary recompositions slow down the app
  • Especially noticeable in complex UIs
  • Affects battery life and user experience

Compose Optimization Strategy

Compose uses two mechanisms:

  1. Compile-time stability analysis → determines if class is stable
  2. Runtime reference equality → compares object instances

Stable classes enable both optimizations.

The Fix

Instead of mutable constructor properties:

// Don't do this
data class User(var name: String)

Use immutable properties:

// Do this
data class User(val name: String)

For mutable state, use external state management:

// In ViewModel or Composable
var userName by mutableStateOf("")
val user = User(name = userName)

Key Takeaways

  • var in data class constructors = performance penalty in Compose
  • val properties = stable classes = better performance
  • Use Compose compiler metrics to verify optimizations
  • Keep UI models immutable for predictable behavior
  • Move mutable state outside data class constructors

Simple Rule

Data classes for UI models should only use val properties.

This ensures optimal Compose performance and prevents hard-to-debug recomposition issues.