Google has made Android UI development “Compose First”, and has explicitly said the legacy View toolkit components that Compose replaces are now in maintenance mode. (android-developers.googleblog.com) If you’re running a real product, that creates a decision: either keep shipping on Views while the ecosystem’s centre of gravity moves away from you, or migrate without accidentally buying a multi-year rewrite.
This is the migration plan that works in practice: isolate legacy View screens behind stable boundaries, convert a small number of high-leverage surfaces first, and be ruthless about not creating a permanent “two UI systems” architecture.
What “maintenance mode Views” actually changes (and what it doesn’t)
Compose-first doesn’t mean your existing View code stops working next quarter. It does mean that:
- Future guidance, samples and many new libraries will assume Compose, not XML + View hierarchies. (android-developers.googleblog.com)
- The View components that Compose replaces (think
android.widget.*) are positioned to receive only critical fixes rather than ongoing feature development. (android-developers.googleblog.com)
What it does not mean:
- You must rewrite your UI immediately.
- You cannot mix systems. Compose was designed to interoperate with Views so you can migrate incrementally. (developer.android.google.cn)
The founder-level risk here isn’t “will Views be removed”. It’s “will we be the last team paying the tax for an increasingly non-standard stack”. That tax shows up as hiring friction, slower UI iteration, and a growing pile of one-off workarounds whenever a new Android capability lands with Compose-first examples.
The goal: one UI architecture, temporarily two toolkits
The most common failure mode I see is teams treating migration as “we’ll add Compose, and then some day we’ll see where we are”. That’s how you get stuck with:
- Two parallel design systems (XML styles vs Compose theme tokens).
- Two navigation approaches.
- Two sets of UI testing patterns.
- Engineers who only feel productive in one half of the codebase.
Your target end-state should be: one set of boundaries and contracts (state models, events, navigation interfaces, analytics hooks), with a gradual swap of the rendering layer from View to Compose.
That’s why interoperability is both a gift and a trap. Google provides official APIs to embed Compose in Views (ComposeView) and to embed Views inside Compose (AndroidView, AndroidViewBinding). (developer.android.com) Used well, they let you keep shipping. Used casually, they create a permanent hybrid.
Step 1 — Define “stable boundaries” around legacy screens
Before you touch a screen, define the boundary you want it to live behind. This is the part that de-risks everything.
A practical boundary for most apps is:
- A screen-level state model (data the UI needs to render)
- A small set of UI events (user intents)
- A navigation interface (what this screen can request)
The key is that the boundary must not expose View-specific primitives.
What to ban at the boundary
If you allow these to leak out, you will struggle to ever delete Views:
View,Context,Resourcesaccess for “formatting”- Passing
Drawable/Spannable/TextWatcheracross layers - Screen logic embedded in adapters, custom views, or binding expressions
Instead, push formatting into:
- Pure Kotlin formatting functions
- A UI model that already contains display-ready strings (still locale-aware, just not View-dependent)
You will still need Context somewhere (e.g., for resources), but treat it as an implementation detail inside a renderer, not as an architectural dependency.
Step 2 — Choose your migration direction deliberately
There are two legitimate directions:
- Compose inside existing View screens (small Compose islands)
- Views inside new Compose screens (Compose surface, legacy widgets embedded)
Google’s migration guidance is explicitly incremental, with Compose and Views co-existing until you’re fully migrated. (developer.android.google.cn) The trick is deciding which way reduces long-term hybrid complexity.
When “Compose inside View” is the right first move
Use ComposeView when:
- Your navigation and screen containers are stable (Activities/Fragments you don’t want to touch yet).
- You want to replace discrete components with high UI churn (e.g., a card, a filter bar, an empty state).
- Your team is still learning Compose and you want a low-blast-radius start.
This keeps the app structure familiar while you build Compose muscle.
When “View inside Compose” is the right first move
Use AndroidView / AndroidViewBinding when:
- You want the screen itself to be Compose (so your state, layout, preview/testing patterns standardise quickly).
- You have a small number of legacy widgets that are expensive to replace immediately (maps, camera previews, complex third-party views).
Google documents both embedding approaches as first-class interoperability APIs. (developer.android.com)
The warning: AndroidView is often where “temporary” becomes permanent. Treat embedded Views as a debt item with an owner and an exit plan.
Step 3 — Pick the 3 highest-leverage Compose conversions
Most teams pick migrations by ease (“this screen is small”). That’s how you spend six months migrating screens that nobody changes, while your hardest screens keep rotting.
Pick conversions by leverage. Three good categories:
- Design system foundations - Theme tokens (colour, typography, spacing) - Common primitives (buttons, text fields, top bars)
If you do this early, every subsequent Compose screen is cheaper and more consistent. It also forces the uncomfortable decisions about how you map XML styles to Compose theming.
- One high-traffic, high-change screen - Home / feed / search results
These screens pay back fast because they are where the product team will ask for changes every sprint. If Compose improves iteration speed for you, this is where you will feel it.
- One “thorny” screen that defines your hybrid risk - The screen that currently forces you to keep complicated View infrastructure: RecyclerView + multiple view types, nested fragments, custom gesture handling, heavy databinding expressions.
If you can’t name this screen, you’re not planning; you’re hoping.
Step 4 — Set explicit rules to avoid “two UI systems forever”
Hybrid is fine. Permanent hybrid is not.
Here are rules that keep the migration converging:
- No new screens in Views from the date you start. Existing screens can be maintained, but new development goes Compose-first.
- One direction per feature: either “Compose screen embedding legacy View widgets” or “View screen hosting Compose components”. Mixing both inside the same feature multiplies lifecycle and state bugs.
- A deadline for each embedded View: if you use
AndroidView, create a ticket that states the replacement plan (native Compose, or a wrapped library upgrade, etc.). - One design system: even if you still render some View screens, stop evolving XML-only style tokens.
If you cannot enforce these rules, do not attempt a broad migration. Instead, do a narrower one: Compose for new features only, with a firm architectural boundary so that old screens remain contained.
Step 5 — Handle the boring edge cases early (because they will bite you)
Most Compose migrations stall on “not the UI”, but the glue.
The migration guidance calls out real-world integration concerns like what scope you’re migrating at (Activity, Fragment, navigation graph), and how to deal with system insets when Views and Compose coexist. (developer.android.com)
A short checklist to run through in week one:
- Decide where Compose owns window insets and where Views do; don’t let both consume them. (developer.android.com)
- Establish your approach for previews and screenshot tests (Compose makes previews attractive; use that to improve feedback loops).
- Standardise state collection (Flow/LiveData) and lifecycle ownership so you don’t accidentally wire the same state twice.
- Put performance guardrails in place (jank metrics, baseline profiles if you already use them) so you don’t argue from anecdotes.
When the right answer is “do not migrate yet”
There are cases where an aggressive UI migration is the wrong move:
- You’re about to sunset the Android app or heavily pivot the product.
- Your Android team is 1–2 people and already at capacity; a migration will silently turn into a quality drop.
- You rely on a critical third-party View-based UI SDK with complex integration, and you cannot yet wrap it cleanly without risking regressions.
Even then, you should still respond to Compose-first reality with a plan:
- Stop adding new View-only infrastructure.
- Introduce the stable boundaries described above so that when you do migrate, you’re not starting from a tangled ball of UI code.
A practical delivery plan that doesn’t freeze the roadmap
A workable plan for a 20–500 person company usually looks like:
- Weeks 1–2: set foundations - Add Compose to the build, agree boundaries, define theming approach. - Build a small set of Compose primitives that match your existing UI.
- Weeks 3–6: ship the first real slice - Convert one high-leverage screen or feature end-to-end (including analytics, navigation, tests). - Measure what got easier and what got harder; update your rules.
- Ongoing: migrate with intent - Every quarter, pick a small batch of screens to convert. - Keep a visible list of embedded Views and delete them systematically.
Google’s own framing is that Compose is now the standard and future UI development will happen in Compose, while Views are in maintenance mode. (android-developers.googleblog.com) Treat that as an architectural direction, not a panic button.
If you migrate without boundaries, you don’t get “Compose”; you get a permanent interoperability project.
Where Codeversols can help (briefly, and honestly)
If you already have Android engineers, the biggest accelerant is usually not “more hands” but getting the migration shape right: boundaries, sequencing, and the rule-set that prevents permanent hybrid drift. At Codeversols we typically help teams by running a short architecture spike (1–2 weeks) that produces a concrete migration backlog, plus a first production screen conversion that your team can replicate across the app.
Close
Compose-first Android isn’t a reason to rewrite; it’s a reason to stop drifting. Isolate legacy Views behind stable boundaries, convert a small number of high-leverage surfaces first, and be disciplined about interoperability so it stays a bridge rather than a destination. If you do that, you can keep shipping while systematically deleting the parts of your UI stack that are now, by Google’s own definition, in maintenance mode. (android-developers.googleblog.com)



