Pembaruan Over-The-Air (OTA) memungkinkan pengembang React Native mendistribusikan perbaikan JavaScript bundle dan aset secara instan tanpa melalui siklus review App Store atau Google Play Store. Namun, distribusi langsung tanpa guardrail native membawa risiko besar: JavaScript runtime exception yang fatal pada root level dapat memicu crash loop, kondisi di mana aplikasi langsung menutup sebelum runtime sempat mendownload bundle perbaikan.
Untuk mencegah brick pada aplikasi pengguna, arsitektur rilis OTA harus memiliki lapisan observabilitas, penanganan fallback otomatis di level runtime, dan protokol eksekusi rollback darurat.
1. Mekanisme Client-Side Auto-Rollback
Penyebab utama crash loop adalah bundle baru yang langsung mengeksekusi kode fatal sebelum framework pembaruan menandai status rilis sebagai stabil. Solusinya adalah mekanisme two-phase commit pada bundle: bundle dianggap valid hanya jika berhasil menyelesaikan siklus render pertama dan runtime native menerima sinyal bahwa aplikasi stabil.
Implementasi CodePush App Ready
Jika menggunakan Microsoft CodePush, runtime native menyimpan pointer ke dua direktori: currentPackage dan previousPackage. Jika aplikasi crash sebelum method konfirmasi dipanggil, native bridge memulihkan pointer ke previousPackage pada proses booting berikutnya.
import React, { useEffect } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import codePush from 'react-native-code-push';
function RootComponent() {
useEffect(() => {
// ponytail: fallback ke previous bundle otomatis jika call ini tidak tercapai
codePush.notifyAppReady()
.catch((error) => {
console.error('Gagal mengonfirmasi status app ready:', error);
});
}, []);
return (
<View style={styles.container}>
<Text>Aplikasi Berjalan Normal</Text>
</View>
);
}
const codePushOptions = {
checkFrequency: codePush.CheckFrequency.ON_APP_RESUME,
installMode: codePush.InstallMode.ON_NEXT_RESTART,
mandatoryInstallMode: codePush.InstallMode.IMMEDIATE,
};
export default codePush(codePushOptions)(RootComponent);
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
});Catatan: Jangan memanggil notifyAppReady() di luar block lifecycle root atau sebelum inisialisasi state kritikal (seperti global store dan navigation container) selesai dimuat. Jika crash terjadi sebelum pemanggilan ini, client akan otomatis melakukan rollback saat cold start berikutnya.2. Observabilitas Rilis: Metrik Crash-Free Users
Monitoring berbasis log server tidak cukup untuk mendeteksi rilis bundle yang rusak karena crash di level JS sering kali gagal mengirim request network. Observabilitas rilis mobile bertumpu pada metrik Crash-Free Users dan Crash-Free Sessions melalui SDK crash reporting (seperti Sentry, Firebase Crashlytics, atau Datadog).
- Ambang Batas Kritis: Tetapkan threshold Crash-Free Users minimal di angka 99.5%. Penurunan di bawah 99.0% dalam jendela waktu 15 menit pasca rilis adalah trigger otomatis untuk menghentikan distribusi (halt) dan melakukan rollback.
- Release Tagging: Wajib menyematkan identitas bundle hash atau release label ke dalam crash context. Contoh integrasi Sentry:
import * as Sentry from '@sentry/react-native';
import codePush from 'react-native-code-push';
codePush.getUpdateMetadata().then((metadata) => {
if (metadata) {
Sentry.setTag('ota_release', metadata.label);
Sentry.setDist(metadata.label);
}
});3. Prosedur Eksekusi Instant Rollback via CLI
Saat alert mendeteksi anomali pada live bundle, tindakan tercepat adalah membatalkan distribusi via CLI remote control untuk mencegah pengguna lain mengunduh target rilis yang rusak.
App Center / CodePush CLI
Untuk membatalkan rilis terakhir dan mengembalikan pointer deployment ke rilis sebelumnya secara instan:
# Rollback ke versi sebelumnya secara otomatis
appcenter codepush rollback -a <ownerName>/<appName> -d Production
# Atau arahkan deployment secara eksplisit ke target rilis yang stabil (misal: v14)
appcenter codepush rollback -a <ownerName>/<appName> -d Production --target-release v14Expo EAS Update CLI
Jika menggunakan EAS Update, rollback dilakukan dengan mempublikasikan ulang (re-publish) group update yang terbukti stabil ke target channel produksi:
# Mengalihkan pointer channel 'production' kembali ke update group ID yang stabil
eas update:re-publish --channel production --group <STABLE_GROUP_ID>Perintah di atas tidak memerlukan kompilasi native ulang. Server OTA langsung menyajikan bundle manifest stabil lama kepada semua client yang meminta update baru.
4. Pencegahan: Staged Rollout dan Automated Bundle Validation
Mencegah insiden selalu lebih murah daripada mitigasi crash di produksi. Bangun dua filter utama dalam pipeline CI/CD sebelum bundle diunggah ke server OTA.
Staged Rollouts (Distribusi Bertahap)
Jangan pernah meluncurkan update OTA langsung ke 100% user. Gunakan distribusi persentase bertahap:
- Deploy ke 10% basis pengguna aktif.
- Tahan rilis selama 2–4 jam untuk mengumpulkan telemetri crash reporting.
- Jika metrik Crash-Free Sessions tetap di atas 99.5%, naikkan ke 25%, 50%, lalu 100%.
# Deploy CodePush dengan rollout percentage
appcenter codepush release-react -a <ownerName>/<appName> -d Production -r 10%Automated Bundle Validation di CI
Sebelum build artefak diunggah, pipeline CI harus memverifikasi bahwa bundle JavaScript dapat dikompilasi ke bytecode Hermes tanpa error sintaksis atau unresolved import.
# 1. Generate bundle JS
npx react-native bundle \
--platform android \
--dev false \
--entry-file index.js \
--bundle-output /tmp/index.android.bundle
# 2. Validasi kompilasi Hermes bytecode (verifikasi sintaks dan kompabilitas runtime)
$HERMES_ENGINE_PATH/hermesc -emit-binary /tmp/index.android.bundle -out /tmp/index.android.hbc
# 3. Validasi exit code
if [ $? -ne 0 ]; then
echo "Validasi Hermes bytecode gagal! Bundle berisiko memicu crash loop."
exit 1
fi5. Template Postmortem Insiden Rilis Mobile
Gunakan format postmortem berikut setelah insiden crash loop tertangani untuk dokumentasi teknik dan pencegahan berulang:
# [POSTMORTEM] Crash Loop Pasca Rilis OTA Bundle v15 (2024-XX-XX)
## Ringkasan Eksekutif
- Dampak: Crash-Free Users turun dari 99.8% ke 94.2% selama 35 menit.
- Total Terdampak: ~12.500 user sesi gagal dibuka.
- Solusi Cepat: Rollback CLI dieksekusi ke label v14 via EAS/CodePush.
## Timeline Insiden (WIB)
- 14:10: Rilis bundle v15 di-deploy ke channel Production (100% rollout).
- 14:15: Alert Sentry trigger: Crash-Free Users turun ke 98.9%.
- 14:22: On-call engineer mengidentifikasi Root Component TypeError.
- 14:26: Rollback CLI dieksekusi mengembalikan deployment ke v14.
- 14:45: Metrik Crash-Free Users kembali normal ke 99.8%.
## Akar Masalah (Root Cause)
Uncaught TypeError: Akses property undefined pada `user.preferences.theme`
saat cold boot sebelum storage hydration selesai.
## Rencana Tindakan (Action Items)
1. [P0] Tambahkan defensive check dan null safety pada modul theme. (Owner: @dev1)
2. [P1] Terapkan aturan staged rollout wajib (10% -> 50% -> 100%) di CI/CD. (Owner: @devops)
3. [P1] Tambahkan integration smoke-test bundle cold-start di pipeline GitHub Actions. (Owner: @qa)
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!