Legacy refactoring: XML → Compose, Java → Kotlin
Migration is where an agent helps most and can also do the most damage. The help: mechanical conversion is voluminous and tedious for a human. The damage: a wrong migration compiles but changes behaviour.
The official recommendation is incremental migration: Compose and the View system coexist for a while. That is exactly what the interop APIs are for:
- `ComposeView` — putting a Composable inside an existing View-based layout.
- `AndroidView` — using a View component inside a Compose screen.
- `AndroidViewBinding` — using existing view binding from Compose.
| Migration step | Suits the agent | Reason |
|---|---|---|
| Planning the sequence | Partly — in plan mode, with human approval | Business priorities are unknown to the agent |
| Writing tests for current behaviour | Yes — high value | Mechanical, and the result is checkable |
| Converting one screen | Yes | Narrow scope, guarded by tests |
| Converting 20 screens at once | No | Review becomes impossible and regression causes get lost |
| Automatic Java → Kotlin conversion | No — the IDE tool does it better | The agent's value is in the cleanup *after* conversion |
| Making converted Kotlin idiomatic | Yes | Nullability, data classes, scope functions |
The most important rule: don't mix migration with improvement. While converting XML to Compose the agent tends to "improve the logic while I'm here". The PR then blurs conversion and change, and if a regression appears the cause is hard to find. State it explicitly in the task: "don't change behaviour, only convert".
In a Java → Kotlin migration the agent's real value is not the automatic conversion — Android Studio's own tool does that well. The value is in the step after: converted code is technically Kotlin but not idiomatic.
Typical cleanup work:
- Nullability — automatic conversion sprays
?and!!everywhere. Real null-safety analysis is needed. - `data class` — a getter/setter class can often become one.
- Scope functions — simplification with
let,apply,also. - Collection operations — loops replaced by
map,filter,groupBy. - `when` — long
if-elsechains.
All of these carry a risk of changing behaviour, so test coverage matters.
📚 Sources and documentation
- Compose interoperability APIsofficialdeveloper.android.com
`ComposeView`, `AndroidView`, `AndroidViewBinding` and incremental migration.
- Compose migration strategyofficialdeveloper.android.com
- Plan mode and workflowsofficialcode.claude.com