Somewhere right now, a mobile team is debugging a gesture animation that works on iOS but stutters on Android. The React Native bridge is involved, and the fix requires a native module. The engineer who wrote that module left six months ago. The PR will take a week, and both platforms are blocked until it ships.
This is the cost of sharing code between two platforms that don't want to share code.
The industry has spent a decade answering "two platforms, one product" with cross-platform frameworks: React Native, Flutter, Kotlin Multiplatform. They all try to collapse iOS and Android into a single codebase. Write once, run everywhere. Except in practice it's more like write once, debug everywhere, and maintain a bridge forever.
But the problem was never code duplication. It was identity duplication. Two teams building the same feature with no shared source of truth for what that feature is, what it guarantees, what rules it enforces. The code was always supposed to be different. What needed to be the same was the identity.
We tried sharing the wrong thing
Cross-platform frameworks share implementation. That's the premise: one codebase, one set of components, one language, plus a translation layer (bridge, rendering engine, compiler) that maps shared code to platform-specific output.
It works until it doesn't.
Airbnb invested heavily in React Native starting in 2016. Their engineers wrote 80,000 lines of product code across 220 screens, plus 40,000 lines of JavaScript infrastructure. With roughly four times as many native screens on each platform, about 20% of their app surface lived in shared code. The other 80% was still native. The hybrid model created its own problems: navigation between native and React Native screens required custom bridging infrastructure, debugging crossed the JS/native boundary constantly, and the team spent more time maintaining the integration layer than building features.
They sunset React Native in 2018 and went back to fully native development. Udacity made the same call within weeks, citing Android fragmentation and integration friction. Both teams said their experience was specific to their orgs, but the pattern was structural, not situational.
Cross-platform frameworks share the implementation layer. But iOS and Android are different environments with different interaction paradigms and different performance characteristics. SwiftUI and Jetpack Compose look similar on the surface, but gesture handling, navigation patterns, accessibility APIs, and animation systems diverge significantly underneath. The more you push shared code into platform-specific territory, the more bridge infrastructure you maintain. At some point the bridge becomes the product.
The thing that should be shared is identity
We introduced the ribosome layer as the translation machinery between Software DNA (the declarative identity of what a piece of software is) and the code that actually runs. DNA declares what to build: behavioral contracts, business rules, integration points, compliance requirements. The ribosome layer determines how that identity gets expressed in a specific environment, from the language and frameworks down to the quality tooling and deployment constraints.
The same DNA, read through different ribosome layers, produces different code. A payment service's DNA declares idempotent processing and P99 under 200ms. The Go ribosome layer produces Chi handlers and sqlc queries; the Rust one produces Axum handlers and sqlx queries. Same contracts, different implementations, both correct.

Mobile is where this pattern shows up most clearly.
A checkout flow has identity: cart validation rules, payment processing contracts, fraud detection thresholds, accessibility requirements, analytics events. None of that changes between iOS and Android. The business rules, the compliance requirements, the success metrics are all the same.
What changes is how that identity gets expressed. On iOS, it's SwiftUI views, Combine pipelines, StoreKit integration, and UIKit accessibility traits. On Android, it's Jetpack Compose composables, Kotlin coroutines, Google Play Billing, and Android accessibility services. The concurrency models differ. The navigation paradigms differ. So do the animation systems and testing frameworks.
These aren't superficial differences you can paper over with a translation layer. They determine how the app feels. iOS users expect a back-swipe gesture, sheet presentation animations, specific haptic feedback. Android users expect Material Design motion, predictive back gestures, edge-to-edge rendering. Honor these conventions and the app feels native. Abstract them away and it feels like a website wearing a native costume.
The ribosome layer makes this explicit. iOS ribosome layer: SwiftUI, Combine, Swift Testing, SwiftLint strict mode, minimum deployment target iOS 17. Android ribosome layer: Jetpack Compose, Kotlin Coroutines, JUnit 5 with Compose testing, Detekt strict mode, minimum API level 28. Same DNA. Two ribosome layers. Two fully native codebases that don't compromise on platform conventions.
The bridge tax is a maintenance tax
Every cross-platform framework introduces an abstraction boundary between your code and the platform. React Native has the JavaScript bridge (now replaced by JSI in the New Architecture, but the boundary remains). Flutter has its rendering engine that bypasses platform widgets entirely. Kotlin Multiplatform shares business logic but still requires platform-specific UI.
That boundary has a maintenance cost, and it compounds.
The State of React Native survey consistently surfaces ecosystem friction as developers' top pain point: debugging, unmaintained packages, and native code integration. When React Native shipped its New Architecture in 2025, the ecosystem faced a migration over six years in the making. Libraries had to be rewritten. Native modules had to be updated. Teams that had built custom bridges found themselves maintaining compatibility layers on top of compatibility layers.
Day to day, debugging is where you feel it most. A crash in a React Native app might originate in JavaScript, in the bridge layer, or in native code. You end up with Chrome DevTools, Xcode, and Android Studio open simultaneously, reading stack traces that jump between layers. One performance issue means reasoning about three execution environments at once. As Apiko's analysis notes, debugging across the JS and native bridge often requires switching between multiple tools, with stack traces jumping across layers.
These aren't bugs in React Native. They're structural consequences of sharing code between environments that have different execution models. The bridge tax isn't a one-time payment. It's a recurring cost that scales with the surface area of your app.
Agentic economics change the math entirely
The old calculus doesn't hold anymore.
The argument for cross-platform was always economic: two native codebases meant two engineering teams. Double the headcount, double the cost. Cross-platform frameworks promised one team, one codebase, maybe 70-80% code sharing. The bridge tax was the price of cheaper headcount.
But in an agentic development model, the unit of cost isn't headcount. It's token spend.
An agent that reads a checkout flow's DNA and an iOS ribosome layer produces a native SwiftUI implementation. The same agent, same DNA, different ribosome layer, produces a native Jetpack Compose implementation. Two fully native codebases. The cost isn't two engineering teams — it's two generation passes. At current API pricing, where a task that would take a senior developer two hours might consume $5 in tokens, generating the same feature natively for both platforms costs less than one engineer's lunch.
Compare that to the bridge tax. Maintaining native modules. Debugging cross-boundary stack traces. Migrating to new architecture versions. Waiting for third-party libraries to catch up. The ongoing cost of the abstraction layer, measured in engineering hours, delayed releases, and features that get simplified because the bridge can't handle the native API, easily exceeds the token cost of generating native code twice.
The math used to be: one codebase x bridge maintenance < two codebases x two teams. Now it's: one DNA x two ribosome layers x token cost << one codebase x bridge maintenance. That inequality flipped. The bridge is the expensive part now.

This doesn't mean you fire your mobile engineers. It means engineers who used to spend 40% of their time fighting the bridge spend that time reviewing generated code, refining ribosome layers, and improving the DNA declarations that define what gets built. The role shifts from writing bridge-compatible code to curating the identity and constraints that agents translate into native implementations.
Decoupled experiments on shared identity
Once identity is the source of truth, you can run completely different experiments on each platform.
Today, most mobile A/B testing frameworks treat iOS and Android as a single population or as simple segments within the same experiment. Statsig's mobile testing guide notes that iOS and Android users behave differently (iPhone users might respond to minimalist designs while Android users expect Material Design patterns) but the experimentation infrastructure still assumes a shared implementation.
When DNA is the source of truth, experiments become ribosome-layer decisions. The identity declares: "checkout flow converts users from cart to purchase, measures conversion rate and time-to-complete, respects PCI compliance requirements." That's stable. That's the contract.
Now you can run different experiments per platform without diverging on identity:
On iOS, you test a single-screen checkout with inline card scanning using the device camera. The ribosome layer specifies SwiftUI, AVFoundation for camera access, and Apple Pay as the primary payment method.
On Android, you test a two-step checkout with Google Pay integration and a saved-payment-method selector using Credential Manager. The ribosome layer specifies Jetpack Compose, Google Pay API, and Material 3 bottom sheets.
Different hypotheses, different implementations, different user experiences, but the same identity contracts and success metrics. The experiment is the ribosome layer: a controlled variation in how identity gets expressed, tailored to the platform where it runs.

You can't do this cleanly with a cross-platform framework. Shared code resists platform divergence by design. Running different UX experiments on iOS vs. Android means either forking the shared codebase (defeating the purpose) or building platform-specific escape hatches (adding bridge complexity). With identity as the source of truth, divergence is the default. Each platform is free to express the same identity however works best for its users.
What this looks like in practice
Your team is building a media player feature for a streaming app.
The DNA declares:
- Plays HLS and DASH streams with adaptive bitrate
- Supports offline download with DRM (Widevine/FairPlay)
- Tracks playback position for resume across devices
- Reports viewing analytics: play, pause, seek, completion events
- Enforces concurrent stream limits per subscription tier
- Accessibility: VoiceOver/TalkBack labels on all controls, caption support
Now apply the iOS ribosome layer:
- AVPlayer with AVKit for playback
- FairPlay Streaming for DRM
- SwiftUI player controls with custom overlay
- Swift Testing, 85% coverage minimum
- SwiftLint strict mode
- Background audio via AVAudioSession
- Picture-in-Picture using AVPictureInPictureController
Apply the Android ribosome layer:
- ExoPlayer (Media3) for playback
- Widevine DRM via MediaDrm
- Jetpack Compose controls with Material 3 theming
- JUnit 5 with Compose UI testing, 80% coverage minimum
- Detekt strict mode
- Background playback via MediaSession and foreground service
- Picture-in-Picture using PiP Activity API
Same identity. DRM contracts are honored on both platforms, just through different systems (FairPlay vs. Widevine). The analytics events are identical (same event names, same properties, same schema) but emitted from AVPlayer delegate callbacks on iOS and ExoPlayer AnalyticsListener on Android. Accessibility requirements are met through platform-native APIs, not cross-platform abstractions that approximate both and satisfy neither.
An agent reads the DNA and the iOS ribosome layer and generates a Swift package with AVKit integration, FairPlay key exchange, and SwiftUI controls that pass SwiftLint and hit 85% coverage. The same agent reads the same DNA with the Android ribosome layer and generates a Kotlin module with ExoPlayer, Widevine setup, and Compose UI that passes Detekt and hits 80% coverage.
Two generation passes. Two native implementations. Zero bridge code. If next quarter the iOS team wants to experiment with a spatial audio player for Vision Pro while the Android team tests a casting-first experience for Android TV, they just write new ribosome layers. The DNA doesn't change. The identity holds.
Two codebases was always the right answer
The industry treated "two codebases" as a problem to solve. It built an entire ecosystem of frameworks and bridges and translation layers to avoid it. Teams spent years paying the maintenance cost of that avoidance.
Two codebases was always the right answer. The problem was that without shared identity, two codebases meant two divergent products. Features drifted. Business logic diverged. One platform got the fix and the other didn't, and nobody noticed until a customer complained.
Shared identity solves the divergence problem without introducing the bridge problem. DNA defines what the feature is and what it guarantees. Ribosome layers are platform-specific and owned by teams (or agents) that understand each platform deeply. The code diverges, intentionally and beneficially, while the contracts stay aligned.
Cross-platform frameworks were a reasonable detour given the economics of the time. But token spend has replaced headcount as the binding constraint, and the thing that actually needed to be shared (identity, not implementation) finally has a layer where it can live.
What to do Monday morning
Pick one feature that exists on both platforms. Something with clear business rules — a checkout flow, a content player, an onboarding sequence.
Write down the identity, not the implementation. What does this feature guarantee? What are the business rules? What events does it track, what compliance requirements does it honor, and how do you measure success?
Now look at the implementation on each platform. Everything that's the same across platforms but expressed differently is DNA. Everything that's specific to iOS or Android (the frameworks, the platform APIs, the testing tools, the deployment constraints) is the ribosome layer.
Version-control the DNA separately from both implementations. Next time that feature changes, change the DNA first. Let each platform's ribosome layer determine how the change gets expressed. What used to be "sync the feature across platforms" becomes "update the identity and re-translate."
The bridge was never the answer. It was an expensive workaround for not having a shared identity layer. Now you have one. Use it.
If you're running native teams that are tired of cross-platform trade-offs, or cross-platform teams that are tired of the bridge, we'd like to hear what you're seeing.
