By NHI Mgmt Group Editorial TeamPublished 2026-03-04Domain: Governance & RiskSource: PassBolt

TL;DR: Moving Android from XML plus MVP to Compose plus MVI and consolidating iOS onto modern SwiftUI was really an architecture and testing reset, with navigation and concurrency debt shaping the hardest work, according to Passbolt. The lesson for identity teams is that legacy coexistence patterns always raise operational cost until the old model is fully removed.


At a glance

What this is: A migration case study showing that modern UI adoption is really an architectural refactor, with state flow, navigation, tests, and concurrency debt driving the outcome.

Why it matters: It matters to IAM practitioners because the same coexistence, boundary, and lifecycle problems show up in NHI, autonomous, and human identity programmes when old and new control models run side by side.

By the numbers:

  • A 2025 IJARSCT paper reports 30 to 40% less UI code and up to 50% faster implementation for complex UI components with Jetpack Compose compared to View-based UI.
  • Compiler warnings dropped from about 1500 to about 8 during Passbolt's Swift 6 migration.

👉 Read Passbolt's migration story on modern Android and iOS UI architecture


Context

Modern UI migration is rarely just a visual refresh. When state handling, navigation, and testing patterns are tied to an older architecture, the real work is redesigning how the application behaves underneath the interface. This is a migration story about platform architecture, not a product announcement.

For identity programmes, the pattern is familiar: coexistence creates friction. Whether the subject is human IAM, NHI lifecycle controls, or agentic AI governance, the hardest part is usually the transition layer where old and new models must operate together before the legacy path is removed.


Key questions

Q: How should teams plan a UI architecture migration without creating more legacy debt?

A: Start by identifying the state, routing, and test boundaries that the old architecture depends on, then redesign those boundaries before converting screens. Migrate in small modules, keep the bridge surfaces narrow, and remove the legacy path as soon as the new model is stable. The goal is architectural exit, not prolonged coexistence.

Q: Why do mixed UI stacks become harder to govern over time?

A: Mixed UI stacks create duplicate patterns, inconsistent navigation, and multiple testing strategies, which increases operational load each time a new screen is added. The longer both stacks remain active, the more the transition layer becomes permanent. Governance fails when coexistence turns into a steady state instead of a temporary migration phase.

Q: How can teams tell whether a migration has actually improved maintainability?

A: Look for fewer glue layers, a single dominant state model, and tests that no longer depend on legacy framework behaviour. If navigation, auth handoffs, and module boundaries still require special-case treatment, the architecture is still split. Maintainability improves when the new model becomes the only model that matters for future work.

Q: What should engineering leaders do when compiler warnings surface during a platform migration?

A: Treat warning cleanup as part of the migration, not as polish. Compiler warnings often reveal hidden assumptions about threading, lifecycle, or dependency ordering, so the work needs time and ownership. If you delay the cleanup, the migration becomes slower later because unresolved debt keeps reappearing in harder modules.


Technical breakdown

Declarative UI changes the state contract

Declarative UI frameworks such as Jetpack Compose and SwiftUI reverse the old control flow. Instead of the UI being pushed into shape through imperative updates, the screen renders from a state model and reacts to intent. That changes how teams reason about presentation, because state becomes explicit, testable, and reusable. In practice, the migration is not about replacing widgets. It is about changing the contract between presentation and logic so that state flow, navigation, and lifecycle handling can be reasoned about in one direction.

Practical implication: redesign the presentation layer around explicit state streams before converting screens one by one.

Navigation is where mixed architectures break down

Navigation is often the first place where legacy and modern architectures collide. In a mixed stack, each screen may follow a different pattern for routing, back handling, and auth handoff, which makes the application feel inconsistent even when individual modules are sound. The article shows that modular coexistence can work, but only while the bridge surfaces remain limited and tightly controlled. Once navigation spans both old and new models, the migration stops being a UI rewrite and becomes a system integration problem.

Practical implication: treat navigation as a migration control point and isolate mixed-routing paths until the legacy stack is retired.

Strict concurrency surfaces hidden debt

Swift 6 strict concurrency is a good example of a compiler exposing problems that were already present but easy to ignore. Concurrency checks do not just add warnings. They force the team to confront thread-safety assumptions, dependency injection complexity, and code paths that relied on manual discipline. That makes the migration slower at first, but it also reveals where the codebase had been depending on implicit behaviour. The value is less about syntax and more about eliminating race-condition risk and making shared state boundaries explicit.

Practical implication: budget time for warning cleanup and structural refactoring, not just feature parity.


NHI Mgmt Group analysis

Legacy coexistence is the real migration debt. The article shows that the hardest work was not rewriting screens but removing the cost of having two architectures active at once. MVP and MVI on Android, and multiple UI generations on iOS, created bridge logic, testing drift, and longer decision paths. For identity teams, this is the same pattern that appears when old and new control models are allowed to coexist too long. Practitioner conclusion: the migration cost is driven by overlap, not by the destination architecture alone.

Navigation is the control plane of the client application. Passbolt's experience makes navigation look like an implementation detail, but it is actually where architectural assumptions meet user flow, auth, and module boundaries. When that layer changes, the whole system feels the impact because routing defines how state, security, and user context move across the app. Practitioner conclusion: migration plans should treat routing and handoffs as first-class architecture, not cleanup work.

Strict concurrency exposes hidden trust in shared state. The Swift 6 effort shows that compiler-enforced concurrency does more than improve code quality. It forces teams to confront where the application had been relying on implicit sequencing, manual caution, or fragile dependency chains. That is the same governance lesson identity architects face when they move from loosely managed privilege to explicit lifecycle control. Practitioner conclusion: the longer implicit state is tolerated, the more expensive the eventual control reset becomes.

State flow redesign is the named concept that matters here. The migration worked because the team replaced bidirectional, framework-specific behaviour with explicit state flow and intent handling. That pattern improves predictability, but only after the presentation layer is rebuilt around it and test strategy is rewritten to match. Practitioner conclusion: teams should evaluate migration success by whether state boundaries are now explicit and portable, not by whether the UI simply looks modern.

Parallel architectures are manageable only as a short-lived transition state. The article shows that staged rollout can reduce risk, but the hidden cost is extra dependencies and mental load until the last legacy module is gone. That matters for any governance programme that tolerates dual paths for too long. Practitioner conclusion: if coexistence becomes normalised, the migration has not really finished.

From our research:

  • 97% of NHIs carry excessive privileges, increasing unauthorised access and broadening the attack surface, according to the Ultimate Guide to NHIs.
  • 71% of NHIs are not rotated within recommended time frames, increasing the risk of compromise over time.
  • For the adjacent governance model behind this topic, see NIST Cybersecurity Framework 2.0 for a control-oriented view of change, resilience, and recovery.

What this signals

State flow redesign is the practical lesson for identity teams. When a programme keeps old and new control models alive at the same time, the bridge layer becomes the real source of risk and maintenance cost. That pattern appears in identity lifecycle work as often as it does in application migration, which is why transition design matters as much as the end-state model.

The same governance pressure shows up in NHI programmes when teams retain temporary coexistence patterns for too long. If legacy privilege paths, duplicate auth mechanisms, or parallel tooling are left in place, the result is usually more operational complexity rather than faster delivery.

For practitioners aligning change programmes with control frameworks, NIST Cybersecurity Framework 2.0 remains a useful anchor for framing how architectural change should be governed, measured, and retired.


For practitioners

  • Map the migration as a control-plane change Document where routing, state ownership, and auth handoffs cross old and new stacks before converting additional modules. Use that map to identify the bridge surfaces that must be isolated first, especially where mixed navigation creates inconsistent behaviour.
  • Rewrite tests alongside presentation logic Replace framework-specific UI tests with tests that follow the new state model and selectors. Do not treat test migration as a later phase, because the article shows selector changes and manual regression effort are part of the architecture shift itself.
  • Limit the lifespan of dual architectures Keep legacy and modern stacks modular and time-boxed, then remove the remaining dependencies that keep the old model alive. The goal is not coexistence at scale but a clean exit from the transition path before dependency and cognitive load accumulate.
  • Budget for concurrency cleanup early Expect strict concurrency or equivalent safety checks to surface hidden design debt in dependency injection, shared state, and threading assumptions. Build warning cleanup into the migration plan so the refactor does not stall when the compiler starts forcing hard choices.

Key takeaways

  • UI migration is really an architecture migration, because state, routing, and testing change more than the visual layer.
  • The hardest problems are bridge surfaces and hidden technical debt, not the screen rewrite itself.
  • Programmes that leave coexistence in place too long pay for it in complexity, maintenance, and slower future change.

Standards & Framework Alignment

This section maps relevant standards and security frameworks to the operational risks and controls described in this guidance.

NIST CSF 2.0, NIST CSF 2.0 and NIST Zero Trust (SP 800-207) set the governance and control requirements practitioners need to meet.

FrameworkControl / ReferenceRelevance
NIST CSF 2.0PR.IP-1Migration work depends on managed change and controlled release discipline.
NIST CSF 2.0PR.PT-5The article's concurrency and platform shifts affect platform resilience and hardening.
NIST Zero Trust (SP 800-207)Navigation and state boundaries resemble control-plane segmentation in complex systems.

Validate platform changes against resilience requirements before making the new architecture default.


Key terms

  • Declarative UI: A UI model where the screen is derived from current state rather than updated step by step through imperative commands. In large applications, this makes presentation more predictable because the source of truth is explicit and the UI reacts to state changes instead of being pushed into shape.
  • Unidirectional state flow: A pattern where state moves in one direction from model to UI, and user actions move back as intents or events. This reduces hidden coupling and makes behaviour easier to test because the application does not rely on scattered view mutations to stay consistent.
  • Technical debt: Design shortcuts, old assumptions, and deferred refactors that make future change slower and riskier. In migration work, technical debt is not just messy code. It is the accumulated cost of keeping an older model alive longer than the architecture can comfortably support.
  • Migration bridge surface: A boundary where old and new systems must interoperate during a transition. These surfaces are often where the most friction appears, because they carry navigation, auth, state, or testing differences that were hidden when each architecture stood alone.

What's in the full article

Passbolt's full article covers the operational detail this post intentionally leaves for the source:

  • The module-by-module migration sequence for Android and iOS, including how the team staged the transition.
  • The specific navigation and authentication friction that made the coexistence period more expensive.
  • The concrete testing rewrite from view-based selectors to Compose semantics and test tags.
  • The Swift 6 concurrency cleanup experience, including the warning reduction and harder modules still in progress.

👉 Passbolt's full post covers the migration sequence, testing changes, and the remaining work to retire legacy layers.

Deepen your knowledge

NHI governance, agentic AI identity, and machine identity lifecycle are core topics in our NHI Foundation Level course, the industry's only accredited NHI security programme. If you are responsible for identity security strategy or NHI governance in your organisation, it is worth exploring.
NHIMG Editorial Note
Published by the NHIMG editorial team on 2026-03-04.
NHI Mgmt Group — the independent authority on Non-Human Identity, IAM, and Agentic AI security. nhimg.org