У React Native есть преимущество, которого нет у полностью нативных приложений: часть изменений можно доставить пользователям без ревью магазина, обновив JS-бандл «по воздуху» (OTA). На собесе спрашивают, как это работает, где границы и как устроен обычный релиз.
Из чего состоит приложение
- Нативная часть: скомпилированный код React Native, нативных модулей, Hermes. Меняется только новой сборкой через магазин.
- JS-бандл и ассеты: ваш код (в Hermes скомпилирован в байткод), картинки, шрифты. Именно его и подменяет OTA.
Обычный релиз
Android: ./gradlew bundleRelease собирает AAB, подписанный ключом загрузки; Google Play с Play App Signing переподписывает приложение своим ключом. versionCode должен расти с каждой загрузкой.
iOS: архив в Xcode с сертификатом дистрибуции и provisioning profile, загрузка в App Store Connect, TestFlight, ревью. CFBundleVersion (номер сборки) растёт с каждой загрузкой.
EAS Build собирает в облаке Expo, сам управляет ключами и сертификатами (или берёт ваши), а EAS Submit отправляет сборку в магазины. Профили в eas.json: development, preview, production. Номер сборки удобно доверить EAS (autoIncrement), чтобы он не зависел от того, кто собирал.
OTA: как работает expo-updates
- При запуске приложение проверяет сервер обновлений (по умолчанию при старте, можно вручную).
- Если есть обновление, совместимое с этой сборкой, оно скачивается.
- Применяется при следующем запуске (или сразу через
reloadAsync, если вы решили перезагрузить приложение). - Если новый бандл падает при запуске,
expo-updatesоткатывается на предыдущий рабочий.
runtimeVersion определяет совместимость: обновление получат только сборки с тем же runtimeVersion. Если вы добавили нативную библиотеку или обновили React Native, это новая нативная часть, нужен новый runtimeVersion и новая сборка в магазин. Иначе JS обратится к нативному модулю, которого нет, и приложение упадёт. Политика fingerprint вычисляет runtimeVersion по хешу нативной части автоматически.
Каналы и ветки EAS Update: сборка привязана к каналу (production, preview), канал указывает на ветку обновлений. Так можно сначала выкатить исправление на тестовый канал, а потом в продакшен, или раскатить на процент пользователей и откатить.
CodePush
Раньше популярным решением был Microsoft CodePush через App Center. App Center закрыт в марте 2025 года, поэтому новые проекты используют EAS Update или собственный сервер обновлений по открытому протоколу Expo Updates (есть и сторонние self-hosted реализации).
Правила магазинов
Apple и Google разрешают загружать интерпретируемый код (JS), если обновление не меняет основное назначение приложения, не добавляет функций в обход ревью и не обходит защиту и платежи. OTA подходит для исправления багов, текстов и небольших улучшений, но не для «нового приложения внутри старого». Серьёзные изменения проходят через ревью.
Безопасность и качество OTA
- Подпись обновлений (code signing в
expo-updates), чтобы приложение принимало только ваши бандлы. - Поэтапный раскат и мониторинг падений (Sentry, Crashlytics) с привязкой к id обновления.
- Загрузка source maps для каждого обновления, иначе стек-трейсы не разобрать.
- Не делать обязательной перезагрузку посреди действия пользователя.
Частые ошибки
- Отправить OTA, которое использует новую нативную библиотеку, на старые сборки.
- Забыть сменить
runtimeVersionпосле обновления React Native. - Считать OTA заменой магазинного релиза для крупных функций.
- Не загрузить source maps.
- Хранить ключи подписи на ноутбуке одного разработчика.
Что спросят на собесе
- Что можно и что нельзя обновить по воздуху.
- Как
runtimeVersionзащищает от несовместимых обновлений. - Как откатить неудачное OTA-обновление.
- Что разрешают правила App Store и Google Play.
- Как устроен процесс релиза и подписи на Android и iOS.