Главное отличие мобильных релизов от веба: их нельзя откатить мгновенно. Сборка проходит ревью магазина, пользователи обновляются днями, старые версии живут месяцами. Отсюда весь набор практик ниже.
CI для каждого пулреквеста
- Сборка затронутых модулей.
- Линтеры и статический анализ: SwiftLint, detekt, ktlint, Android Lint,
flutter analyze, ESLint. - Юнит-тесты, snapshot-тесты дизайн-системы.
- Проверка размера сборки.
- Цель: 10-15 минут. Полные UI-тесты на нескольких устройствах ночью.
Ускорение: кеш зависимостей и результатов сборки, разделение по модулям, параллельные задачи.
Релизный пайплайн
тег или отсечка → версия и номер сборки → сборка и подпись
→ загрузка dSYM / маппинга R8 в крэш-аналитику
→ TestFlight и внутренний трек Google Play → регресс
→ ревью магазина → поэтапная раскатка → 100% или пауза
Инструменты: fastlane, Gradle Play Publisher, Xcode Cloud, GitHub Actions, GitLab CI, Bitrise.
Подпись: ключи и сертификаты в секретах CI, а не в репозитории. На Android Play App Signing хранит ключ подписи приложения у Google, а команда использует ключ загрузки, который можно сменить при утечке. На iOS API-ключ App Store Connect и общее хранилище сертификатов.
Релизный поезд
Релизы выходят по расписанию (раз в одну-две недели). Что готово к отсечке и закрыто флагами, уезжает; остальное едет следующим поездом. Маленькие регулярные релизы менее рискованны.
Фича-флаги
Типы:
- релизный: прятать незавершённую фичу в основной ветке;
- kill switch: мгновенно выключить проблемную функцию;
- эксперимент: A/B-группы;
- постепенное включение: растущая доля пользователей;
- конфигурационный: лимиты, тексты, параметры без релиза.
Гигиена: у флага владелец и срок удаления, значения по умолчанию безопасны офлайн, флаги кешируются при старте, старые флаги удаляются.
Поэтапная раскатка
Google Play staged rollout: долю задаёте сами, раскатку можно остановить. App Store phased release: 7 дней по фиксированному графику, можно приостановить. На каждом этапе смотрят crash-free users и sessions, ANR rate, бизнес-метрики и отзывы, сравнивая с прошлым релизом.
Хотфикс
Остановить раскатку, выключить фичу флагом или поменять серверную конфигурацию, собрать исправление от релизной ветки, запросить ускоренное ревью, перенести фикс в основную ветку.
Совместимость версий
В продакшене одновременно живут 5-10 версий приложения. Поэтому:
- API меняется аддитивно, ломающие изменения через новую версию эндпоинта;
- клиент устойчив к неизвестным полям, значениям enum и типам блоков;
- поведение настраивается удалённым конфигом;
- минимальная поддерживаемая версия и принудительное обновление только для опасных случаев (уязвимость, сломанный протокол), с мягким предупреждением заранее;
- контрактные тесты между клиентом и сервером.
Server-driven UI позволяет менять экраны (витрины, главные страницы) без релиза, но усложняет совместимость и отладку, а менять суть функциональности в обход ревью правила магазинов не позволяют.
Что спросят на собесе
- Как устроен ваш CI/CD для мобильного приложения?
- Зачем фича-флаги и какие они бывают?
- Как работает поэтапная раскатка и по каким метрикам решать?
- Что делать с критическим багом в релизе?
- Как поддерживать совместимость бэкенда со старыми версиями приложения?