Situation: At my last company I led frontend work for a consumer shopping product delivered as a React web app and React Native apps for Android/iOS. We needed consistent business logic, shared product catalog and cart behavior, but native UIs and platform-specific features (payments, push, deep links).
Task: Decide what to share, ensure compatibility, and create reliable CI/build/test pipelines.
Action:
- Code-sharing strategy: Created a monorepo (Yarn workspaces). Shared pure TypeScript packages for domain logic (pricing, promotions, validation), API client, and types. UI components lived in platform-specific packages: React web components and React Native components that consumed the same shared logic. Where native functionality was required (camera, payments), we wrapped platform differences behind a small adapter interface in the shared package and implemented adapters per platform.
- Testing across environments: Unit tests with Jest for shared logic. Web E2E with Cypress and component tests with React Testing Library. Mobile E2E with Detox on emulators and a device farm (AWS Device Farm) for real-device coverage. We used BrowserStack for cross-browser checks (Chrome, Firefox, Safari, Edge) across OS versions.
- Build & CI considerations: CI pipeline in GitHub Actions. Matrix builds: web, android, ios. Caching for node_modules and Gradle, parallel jobs for faster feedback. Android artifacts built with Gradle, iOS with fastlane for code signing and TestFlight distribution. Publish shared packages to an internal npm registry on tag merge. Enforced linting, type checks, and test thresholds as required CI gates.
- Compatibility problems & solutions:
- Floating-point price rounding differences between JS engines: moved all money calculations to integer cents in shared logic and added strict unit tests.
- Date/time timezone bugs on iOS WebView vs native: standardized on ISO strings and used date-fns with explicit UTC handling in shared layer.
- Styling differences (e.g., font metrics, touch targets): built a design-token package with platform mappings; web used rem-based tokens, mobile used dp-scaled equivalents.
- Web-specific accessibility: added ARIA attributes and keyboard focus management in web components; mobile got native accessibility props mapped from tokens.
- Push notifications: abstracted registration and payload handling into adapter; unified payload schema in shared types.
Result: Reduced duplicated logic by ~60%, eliminated several cross-platform bugs before release, increased E2E coverage to 85%, and accelerated release cadence—web and mobile releases synchronized within the same sprint with far fewer regressions.
Learning: Share pure logic and types, abstract platform differences behind small interfaces, invest in device/browser automated testing, and optimize CI with caching and parallelism to keep feedback fast.