CI/CD & Release Management Strategy
📖 Concept
CI/CD for React Native is more complex than web because you're building for two platforms (iOS + Android), managing native dependencies, and dealing with app store review processes.
CI Pipeline stages:
PR Created
→ Lint (TypeScript, ESLint, Prettier check)
→ Unit Tests (Jest, ~2 min)
→ Integration Tests (RNTL + MSW, ~5 min)
→ Build Check (Metro bundling, ~3 min)
→ [Optional] E2E Tests on PR (Detox, ~15 min)
Merge to main
→ All above
→ E2E Tests (full suite, ~30 min)
→ Build iOS (Xcode, ~15 min)
→ Build Android (Gradle, ~15 min)
→ Upload to TestFlight / Firebase App Distribution
Release Branch
→ Full test suite
→ Code signing
→ Store submission (App Store Connect, Google Play Console)
→ [OTA] CodePush for JS-only changes
Release strategies:
- Full store release: Native + JS changes. 1-7 days review time. Staged rollout (1% → 10% → 100%).
- OTA update (CodePush): JS-only changes. Instant delivery. No store review. Use for bug fixes, content changes.
- Feature flags: Ship code behind flags. Enable/disable without any release. Gradual rollout.
Monitoring & observability post-release:
- Crash-free rate (Sentry, Crashlytics) — target >99.5%
- ANR rate (Android vitals) — target <0.5%
- App startup time — monitor p50, p95, p99
- Bundle size tracking — alert on >5% increase
- API error rates — dashboard per endpoint
- User-reported issues — in-app feedback, support tickets
💻 Code Example
1// === CI/CD CONFIGURATION ===23// .github/workflows/ci.yml (GitHub Actions)4const ciConfig = {5 name: 'CI',6 on: {7 pull_request: { branches: ['main', 'develop'] },8 push: { branches: ['main'] },9 },10 jobs: {11 lint_and_test: {12 'runs-on': 'ubuntu-latest',13 steps: [14 { uses: 'actions/checkout@v4' },15 { uses: 'actions/setup-node@v4', with: { 'node-version': '20' } },16 { run: 'npm ci' },17 { run: 'npm run lint', name: 'ESLint' },18 { run: 'npm run typecheck', name: 'TypeScript' },19 { run: 'npm test -- --coverage', name: 'Unit Tests' },20 {21 name: 'Coverage Gate',22 run: 'npx coverage-threshold --statements 80 --branches 70',23 },24 ],25 },26 build_android: {27 'runs-on': 'ubuntu-latest',28 needs: 'lint_and_test',29 if: "github.event_name == 'push'",30 steps: [31 // Setup Java, NDK, build, upload to Firebase App Distribution32 ],33 },34 build_ios: {35 'runs-on': 'macos-latest',36 needs: 'lint_and_test',37 if: "github.event_name == 'push'",38 steps: [39 // Setup Xcode, fastlane, build, upload to TestFlight40 ],41 },42 },43};4445// === RELEASE MANAGEMENT ===4647// Version management strategy48// package.json version = marketing version (1.2.3)49// Build number auto-incremented by CI5051// Release checklist (automated where possible)52const releaseChecklist = {53 preRelease: [54 'All tests passing on release branch',55 'No P0/P1 bugs open',56 'Release notes written',57 'Staging build tested by QA',58 'Performance benchmarks within thresholds',59 'Bundle size within budget',60 'Crash-free rate on staging > 99.5%',61 ],62 release: [63 'Tag release in git (v1.2.3)',64 'Build signed APK/IPA',65 'Submit to App Store Connect',66 'Submit to Google Play Console',67 'Staged rollout: 1% for 24h',68 'Monitor crash rates, ANR rates, support tickets',69 'If metrics ok: 10% for 24h → 50% → 100%',70 ],71 postRelease: [72 'Verify analytics events flowing',73 'Monitor error rates for 48h',74 'Close release-related tickets',75 'Update internal documentation',76 'Start next release branch',77 ],78};7980// OTA updates with CodePush81import codePush from 'react-native-code-push';8283const codePushOptions = {84 checkFrequency: codePush.CheckFrequency.ON_APP_RESUME,85 installMode: codePush.InstallMode.ON_NEXT_RESTART,86 // Don't force immediate install — user might be mid-action87};8889// Wrap root component90const App = codePush(codePushOptions)(AppRoot);9192// For critical fixes — install immediately93async function deployHotfix() {94 const update = await codePush.checkForUpdate();95 if (update && update.isMandatory) {96 await update.download();97 update.install(codePush.InstallMode.IMMEDIATE);98 // Shows update dialog and restarts app99 }100}
🏋️ Practice Exercise
CI/CD & Release Exercises:
- Set up a GitHub Actions CI pipeline that runs lint, typecheck, and tests on every PR
- Configure Fastlane for automated iOS TestFlight deployment
- Set up CodePush and deploy a JS-only update to a staging environment
- Create a release checklist that includes bundle size comparison with the previous release
- Implement a staged rollout strategy with automated rollback on crash rate increase
- Set up monitoring dashboards for crash rate, startup time, and API error rates
⚠️ Common Mistakes
Not caching node_modules and native dependencies in CI — builds take 20+ minutes instead of 5
Using CodePush for changes that include native code — native changes require a full store release
Not testing on real devices in CI — simulators miss device-specific bugs (camera, GPS, Bluetooth)
Skipping staged rollout — deploying to 100% immediately means a bug affects all users instantly
Not tracking bundle size over time — gradual increases go unnoticed until the app exceeds size limits
💼 Interview Questions
🎤 Mock Interview
Mock interview is powered by AI for CI/CD & Release Management Strategy. Login to unlock this feature.