From MVP to MVI, Java to Kotlin: The Migration Pitfalls That Cost a Year
Teams adopting MVI and Kotlin on legacy Android code should budget for a two-week first page, a three-day learning curve on subsequent pages, and a complete rewrite of presenter tests. The biggest hidden cost is retraining developers to stop manipulating views directly.
Migrating an Android project from MVP to MVI and from Java to pure Kotlin is not a framework swap. The core change is the direction of data flow: from two-way imperative calls where a Presenter pokes at View methods, to a unidirectional loop where UI emits Intents, a ViewModel produces State, and UI renders from that State. That shift forces developers to abandon direct View manipulation, and the source reports that anyone comfortable with callbacks will struggle.
The Java-to-Kotlin conversion introduces platform types that don't enforce null safety, leading to runtime NPEs that are harder to trace. Auto-converted code compiles but isn't safe. The MVI migration also requires modeling mutually exclusive states, which data classes handle poorly, and events like toasts must be separated from StateFlow to avoid re-triggering on rotation. Coroutine misuse, such as swallowing CancellationException or ignoring race conditions, creates subtle bugs. The source recommends progressive migration, starting with a simple page, and rewriting all Presenter tests because old tests are obsolete.
The most time-consuming part of the migration is refactoring discrete UI commands into a single state object, suggesting the difficulty lies not in Kotlin syntax but in the discipline of modeling all UI conditions as data.
The advice to start with a simple, non-core page and accept a two-week cost for the first one indicates that migration costs are front-loaded; the second page takes three days, so the slope flattens quickly once a template exists.
Separating state from events is a general pattern for any unidirectional data flow: putting one-shot events in StateFlow creates sticky-event bugs that surface on rotation, making Channel a better fit for transient UI feedback.
The source's warning about coroutine races implies that MVI's asynchronous nature amplifies existing concurrency issues from MVP, so teams must explicitly manage cancellation and request ordering or hide subtle bugs.
Isn't it all just Kotlin + MVVM anyway?