A composite reference architecture for a B2C subscription SaaS mobile client - team collaboration, habit tracking, or lightweight CRM. The stack is pinned to Expo SDK 57 (expo ~57.0.4), React Native 0.86.0, and React 19.2.3. This is not one shipped product; it distills patterns that recur across production SaaS apps on EAS.
Screen mount → useQuery(key) → Query cache hit? ├─ yes + fresh → render ├─ yes + stale → render + background refetch └─ no → client.ts fetch → API → cache write → render
Release path:
Git tag v2.4.0 → eas build --profile production → store submit │ └─ same runtime → eas update --channel production (JS hotfix)
Cold start loads the root _layout.tsx, which reads session from SecureStore, hydrates Query queryClient, and redirects between (auth) and (app) groups. Route files stay thin - they re-export feature screens per Mobile Architecture Basics.
One production rollback in quarter one - bad paywall copy; reverted channel pointer in 12 minutes
Incident class avoided
No runtimeVersion mismatch strandings after adopting appVersion policy
Paywall modal route (presentation: "modal") shipped without navigator JSX churn - product could A/B SKU params via OTA while native StoreKit config stayed stable.
Pin SDK 57 in the narrative and in package.json. Upgrade notes belong in ADRs; case studies without version pins age badly - annotate every architecture diagram with the SDK that shaped it.
Thin routes, fat features. The highest churn was src/features/inbox/, not app/(app)/(tabs)/inbox.tsx. Onboarding improved when new engineers learned the filesystem for URLs only.
Channels are environments, not engineers. Renaming jane-dev to preview eliminated accidental production OTA publishes.
Query staleTime is a product knob. Inbox at 30s stale, settings at 5m - one global staleTime caused unnecessary battery drain.
Rebuild dev clients after native-affecting bumps. SDK 57 Reanimated and expo-image updates required new development profile binaries; Metro-only refreshes lied about native behavior.
Document what OTA cannot change. Marketing once requested a new camera permission via OTA - blocked until store build; now listed in release checklist Tier 1.