MVP Factory
ai startup development

Kotlin Name-Based Destructuring: The Silent Bug Fix Most Devs Missed

KW
Krystian Wiewiór · · 5 min read

TL;DR

Kotlin’s data class destructuring has been position-based since day one. Reorder your properties, and every destructuring call silently breaks. No compiler error, no warning, just wrong values assigned to wrong variables. Kotlin 2.3.20 ships an experimental name-based destructuring syntax that fixes this. It’s not stable yet, so here’s what you should do today and what to watch for tomorrow.


The problem: position-based destructuring is a landmine

The most dangerous bugs are the ones that compile cleanly. I’ve been bitten by this one personally.

Consider a simple data class:

data class User(val name: String, val email: String)

val (name, email) = getUser()

This works perfectly until someone refactors the data class:

data class User(val email: String, val name: String)

Now name holds the email and email holds the name. The compiler says nothing. Your tests might not catch it if they don’t assert on specific values. This bug ships to production.

Both name and email are String types, so there’s no type mismatch to flag. The destructuring maps to component1() and component2(), pure positional access. Swap the properties, and every destructuring site silently breaks.

Why this hits Android and KMP teams hard

Data classes are everywhere in Android and Kotlin Multiplatform codebases: API response models, UI state holders, domain entities. A single property reorder during a refactor can corrupt data flow across layers without a single compiler warning.

The fix: name-based destructuring in Kotlin 2.3.20

Kotlin 2.3.20 introduces an experimental name-based destructuring syntax that binds variables to property names instead of positions:

val (val mail = email, val username = name) = getUser()

Here, mail is bound to the email property and username is bound to the name property, regardless of their declaration order in the data class. Reorder the properties all you want; the bindings hold.

You can also match names directly without renaming:

val (val email, val name) = getUser()

Each variable binds to the property with the same name. Order in the destructuring expression doesn’t matter.

Enabling the feature

Since this is experimental, you need to opt in via a compiler flag:

// In build.gradle.kts
kotlin {
    compilerOptions {
        freeCompilerArgs.add("-XXLanguage:+NameBasedDestructuring")
    }
}

Position-based vs. name-based: a comparison

AspectPosition-based (current)Name-based (experimental)
Binding strategyMaps to component1(), component2(), etc.Maps to property name
Resilience to reorderingBreaks silentlySafe
Renaming a variableAlways allowedUse val alias = propertyName syntax
Compiler supportStable since Kotlin 1.0Experimental in 2.3.20+
Requires opt-inNoYes (compiler flag)
Type safety on reorderOnly if types differAlways safe

The row that matters most is resilience to reordering. Position-based destructuring gives you zero protection when properties share a type. Name-based destructuring removes the problem entirely.

What you should do today

Here’s what most teams get wrong: they see “experimental” and ignore it. But position-based destructuring is in your codebase right now, and it’s already risky.

Until name-based destructuring is stable, use explicit property access:

// Instead of this:
val (name, email) = getUser()

// Do this:
val user = getUser()
val name = user.name
val email = user.email

Yes, it’s more verbose. But it’s refactor-proof today, without any experimental flags. In codebases I’ve worked on, we lint against destructuring data classes with more than two same-typed properties for exactly this reason.

For teams on Kotlin 2.3.20+ who want to experiment, enable the flag in a feature branch and migrate incrementally. Don’t adopt it in production modules until it reaches stable status. Experimental language features can change syntax or semantics between releases.

Where destructuring remains safe

Destructuring is still perfectly fine where position is inherent and stable:

// Maps: key/value is a stable two-element contract
for ((key, value) in map) { ... }

// Pair/Triple: positions are the API
val (first, second) = Pair("a", "b")

The risk is specifically with data classes whose property order is an implementation detail, not a semantic contract.

What to do about it

Audit your destructuring sites. Search your codebase for val ( patterns on data classes with same-typed properties. These are your highest-risk locations. Replace them with explicit property access until name-based destructuring is stable.

Add a lint rule. Configure detekt or a custom lint check to warn on positional destructuring of data classes where adjacent properties share a type. This is cheap insurance.

Track the feature, but don’t ship it yet. Play with name-based destructuring in side projects or experiment branches to learn the syntax. Watch the Kotlin KEEP and release notes for stabilization. When it lands as stable, migrate. You’ll wipe out a whole class of silent bugs in one move.


TAGS: kotlin, android, kmp, multiplatform, architecture


Share: Twitter LinkedIn