PATTERN Cited by 1 source
Feature-flagged dual implementation¶
Pattern¶
When a module (native module, component, integration) can't be made compatible with both the old and new runtime architectures from a single implementation — and a full conversion is out of scope for the current migration step — fork the implementation behind a feature flag. The flag selects which code path runs based on which architecture the build is targeting. Ship both. Add a deprecation warning to the temporary path.
The canonical wiki instance is Shopify's 2025 React Native new- architecture migration for Shopify Mobile + Shopify POS (see sources/2025-09-12-shopify-migrating-to-react-natives-new-architecture).
Forces¶
- Third-party dependency doesn't support both architectures on a single version. Shopify observed this for several modules during their migration — the upstream library shipped a version that worked on one architecture but broke on the other.
- TurboModule conversion is out of scope. Shopify had 40+ native modules and explicitly deferred TurboModule conversion until after shipping the migration ("allows us to determine which modules are still useful and how we can improve their APIs by leveraging the new capabilities").
- Weekly release cadence. The app must ship weekly while the migration is in progress; all forks are short-lived.
Solution shape¶
- Identify the broken modules. "We only made changes to modules that did not work on the new architecture (mostly modules that dealt with UIManager)." Don't fork modules that already work on both.
- Fork the implementation behind a feature flag. One implementation per architecture, selected at build time or first-load.
- Ship both, but conditionally disable on dev. Shopify explicitly says "we used feature flags to conditionally disable functionality in development mode on the new architecture" for third-party deps that didn't yet support both architectures — i.e., the new-arch build in dev runs without the broken dep's feature, preserving the ability to test everything else.
- Add deprecation warnings to the temporary code paths. "We also added deprecation warnings to temporary code paths for post-migration cleanup." The flag removal is discoverable without a scavenger hunt.
- Remove the flag after migration. Post-shipping, the old- architecture branch of the fork becomes dead code and gets deleted.
Costs¶
- Code duplication. Two paths per forked module.
- Test matrix explosion. Both paths need testing until the old one is removed.
- Cognitive load. Engineers touching a forked module need to understand both implementations.
These costs are bounded by the migration window; the pattern is explicitly not a steady-state architecture.
Why not just full TurboModule conversion up front¶
Shopify's reasoning: "TurboModules represent a step forward, [but] they are not mandatory (yet), and with 40+ modules in the Shopify App, we decided to re-evaluate them after the migration." The two changes — new architecture adoption and TurboModule conversion — are orthogonal. Coupling them multiplies the migration's scope. Decoupling them makes each step independently shippable and reviewable. (See concepts/dual-architecture-compatibility for the broader argument about scope discipline during framework migrations.)
Related¶
- systems/react-native — framework context.
- concepts/dual-architecture-compatibility — the concept this pattern operationalizes at the source-code level.
- patterns/dual-architecture-ci-builds — the CI-level companion.
- companies/shopify — company page.