Engineering

Migrating an existing Android app to Jetpack Compose

Back to Blog

Begin with a migration reason, not an adoption target

Compose can make state-driven UI and reusable components easier to express. That is not enough reason to rewrite stable screens. Name the problem first: duplicated UI logic, slow feature work, an upcoming redesign, accessibility debt, or difficulty sharing components across a growing product.

The migration succeeds when it improves that constraint without destabilizing releases. “Percentage of screens in Compose” is not a product outcome.

The incremental migration approach

Google's current migration guidance recommends incremental adoption while Compose and Views coexist. A practical sequence is:

This phased approach keeps your release cadence intact. No team goes dark for months on a rewrite. Every sprint still ships value to users.

ComposeView in existing activities and fragments

The bridge between the old and new worlds is ComposeView. You can drop a ComposeView into any XML layout or add it programmatically to a ViewGroup. Inside it, you write pure Compose code.

In practice, this means you can replace a single RecyclerView item layout with a Compose component while the rest of the screen stays in XML. The parent Fragment does not need to change. The ViewModel does not need to change. Navigation stays the same.

Going the other direction works too. The AndroidView composable lets you embed traditional Views inside Compose screens. This is critical for components like MapView or complex custom Views that are not worth rewriting.

State management for enterprise: StateFlow + ViewModel

Compose needs observable state with clear ownership. StateFlow exposed from a ViewModel is one good option, especially when the existing architecture already uses coroutines. Compose can collect it with collectAsStateWithLifecycle().

This pattern works well for several reasons:

Keep state with the narrowest owner that must survive and coordinate it. A ViewModel is appropriate for screen-level state, but not every toggle, expansion state, or reusable component value belongs there.

Performance considerations

Compose is fast, but careless code will create jank. Recomposition is the main thing to watch. Every time state changes, Compose re-executes the affected composable functions. If your composable reads more state than it needs, it recomposes too often.

Key practices we follow:

Do not optimize from folklore. Capture a baseline, use release builds, test on a representative lower-end device, and profile the interaction that matters.

Testing Compose UI

Compose provides semantics-based UI testing APIs for locating nodes, performing actions, and asserting state. Tests still become flaky when clocks, asynchronous work, navigation, or external services are uncontrolled. Compose changes the tools, not the need to design test seams.

Use plain unit tests for reducers and business rules, Compose UI tests for meaningful component and screen behavior, and a small number of end-to-end tests for critical journeys. Screenshot testing can help with visual regressions, but it needs deliberate device, font, locale, and tolerance controls.

When not to migrate

Not every screen deserves a migration. Some modules should stay exactly as they are.

Pragmatism beats purity. The goal is a better codebase that ships reliable software, not a Compose adoption percentage on a dashboard.

Define the first safe boundary

Choose one screen with active product work, manageable dependencies, and observable performance. Ship it through the existing release path. Record what changed in build time, test stability, accessibility, and development effort before choosing the next boundary.

If your team is planning a migration, contact DEVSFLOW. We can review the current architecture and identify a bounded first stage that preserves the existing release path.

Adnan Javed

Adnan Javed

Senior Android Engineer at DEVSFLOW Technologies

Need a senior Android engineer who knows Jetpack Compose inside and out? Our engineers can embed into your team and help you migrate without disrupting your release cadence. Learn about staff augmentation