Всем привет! Меня зовут Михаил Булгаков, и я работаю в команде релиз-инженеров Badoo. В этом посте я расскажу о том, как происходят релизы iOS-приложений с момента «У меня есть готовый бинарь» до момента «После нас хоть потоп», и, конечно, как это делаем мы в Badoo (забегая вперёд: нам удалось сократить время, необходимое на запуск релиза, с нескольких часов до одной минуты и избавиться от ручной работы).  Для начала коротко расскажу о том, как вообще проходят релизы iOS-приложений. Будем считать, что у нас всего одно приложение с одним поддерживаемым языком. Возможно, кому-то эта часть покажется очевидной, я понимаю. Поэтому если вы знаете, как эта кухня устроена, переключайтесь сразу на следующую главу – iOS-релизы в Badoo. Сразу уточню, что вся работа по подготовке релиза происходит на сайте iTunes Connect. Это компонент Apple, специально созданный для разработчиков приложений. Есть как веб-версия, так и приложение, и у обоих есть недостатки. Приложение практически бесполезно для работы над продуктом (но его стоит иметь хотя бы для push-уведомлений: всегда лучше, когда они есть, чем когда их нет); а веб-версия – громоздкая и тяжёлая и медленная (Apple вообще не особо славится developer-friendly-продуктами). Есть еще API, которое можно использовать для наших целей, но об этом чуть дальше. Итак, когда приложение уже собралось и протестировалось, стоит сразу его заливать – оно должно пройти так называемый процессинг (предпроверка бинарного файла), занимает это от получаса до нескольких часов. Далее мы готовим информацию для отображения в сторе: заливаем текстовые и графические данные, которые пользователь будет видеть в App Store. Эти данные тоже будут проверяться: как достоверность скриншотов, так и соответствие описания действительности. Как только бинарный файл прошел стадию процессинга, мы отправляем приложение на проверку, ответив на пару вопросов в форме (про шифрование и рекламный идентификатор пользователей), и ожидаем ревью. Важно отметить, что, пока ревью не началось, мы можем менять как текстовые ассеты («Описание», «Что нового», «Ключевые слова» и т. д.), так и графику (скриншоты, иконку и всё такое). Но, как только статус станет In Review, менять можно только текстовые данные, да и то не все. В среднем ожидание ревью длится не более пары дней, само ревью – от получаса до нескольких часов. Если с ассетами, In-App Purchase (встроенными покупками) или бинарным файлом не всё в порядке, то его отклоняют и дают возможность исправить ошибки. Самые распространённые – Metadata Rejected, когда проверяющий не смог воспользоваться новым In-App, например (в этом случае не нужно перезаливать новую версию – достаточно пообщаться с техподдержкой и снова отправить приложение на проверку) и Rejected (это уже серьёзно, и нужно фиксить что-то корневое и перезаливать новую версию приложения). Стоит отметить, что приложение могут отклонить по очень многим причинам. Требования Apple очень конкретны и подробно описаны в гайдлайнах. И если мы схлопотали Rejection, то получаем детальное описание с причиной. Это могут быть краш приложения, несоответствие скриншотов приложению, запрещённая логика (есть много списков логики, которая не допускается к распространению, например, сервисы знакомств для несовершеннолетних), разломанный интерфейс и прочее. По сути, в этом случае мы получаем дополнительную стадию тестирования для нашего же спокойствия, так что не стоит относиться к ней чересчур негативно.  Ещё есть такая возможность, как Expedited App Review. Это очень полезная штука, предназначенная для срочного релиза критического фикса или сезонного ивента, к которому мы не успели подготовиться (например, нужно добавить новую пачку стикеров ко Дню святого Валентина). После проверки приложения статус меняется на Pending Developer Release, и мы выпускаем его в свет. Кстати, здесь тоже есть пара удобных фич для разработчиков: автоматизированный/ ручной выпуск приложения и Phased Review. Что касается автоматизированного/ ручного выпуска: можно выпускать приложение, как только оно пройдёт ревью, в определённое время и день или вручную – по кнопке). А второй инструмент – очень интересная штука. Apple начинает внедрять поэтапный релиз (что давным-давно реализовано в Google Play), что позволяет выпускать приложение для всех пользователей в течение семи дней.  Сейчас у нас восемь iOS-приложений (основное – Badoo – и несколько приложений для знакомств под разными брендами, направленных на свою целевую аудиторию или страну, со своими особенностями и плюшками), большинство из них переводится на 25 языков и диалектов. Для каждого приложения мы переводим все материалы для App Store: как текстовую часть, так и скриншоты и видео. Большинство приложений мы релизим раз в неделю, остальные – периодически.  Естественно, для подготовки и релиза такого объёма контента, помимо самого бинарного файла, нужно подготовить и залить в стор огромное количество текстов и картинок. И, само собой, это отнимает много времени у релиз-инженеров, переводчиков и продакт-менеджеров. Без автоматизации это всё нам вылилось бы маленький локальный ад для всех участников процесса. Ниже я расскажу, как мы прошли путь от этого ада до очень удобного и прозрачного флоу.   Пока команда QA занималась тестированием релизной ветки, продакт-менеджер писал тексты для следующего релиза в Google Docs и отправлял их команде переводчиков. Которые, в свою очередь, делали переводы в этом же документе. Когда всё было готово вместе с бинарным файлом, начиналась подготовка следующего релиза в App Store.  Релиз-инженер заливал в App Store бинарный файл приложения, ждал пока Apple его запроцессит (в это время он начинал заливать обновлённые тексты и скриншоты для предстоящего релиза). В среднем процессинг занимал один-два часа. Примерно за это же время мы успевали обновить ассеты для приложения.  Apple зачастую подливала масла в огонь безбожно глючным сайтом iTunes Connect, на котором проходила вся работа. И мы активно использовали лайфхак – слушать релаксирующую музыку. Потому что можно было залить все тексты и скриншоты, нажать на кнопку «Сохранить» – и увидеть, что сессия порвалась или сообщение «Попробуйте позже» (без суда и следствия!) или незаполненные ассеты для несуществующего языка. К слову, такие глюки и сейчас не редкость.  В общем, тяжела и неказиста ручная работа в iTunes Connect, если у вас больше одного приложения на нескольких языках. В чём были явные недостатки такой организации работы:  Дальше начинался процесс мониторинга релизов. Да, Apple высылает письма о каждом изменении статуса приложения. Это удобно. Но тем не менее на этом этапе всё равно требовалось очень много действий и ручной работы, что не позволяло уменьшить вероятность ошибки из-за человеческого фактора. Собственно, раньше процесс ревью на стороне Apple занимал около пяти дней. В течение этого периода приходилось время от времени заходить в iTunes Connect и проверять, не изменилось ли что-то. Письмо можно и потерять, и всё это приходилось делать вручную. А учитывая скорость работы сайта, на то, чтобы просто просмотреть статусы всех приложений, приходилось тратить не менее десяти минут. Просто чтобы просмотреть статусы, Карл! Наконец, когда все приложения успешно проходили ревью, мы оповещали об этом продакт-менеджеров, чтобы они убедились в готовности серверной части и отправили новые версии «в свободное плавание».  Для решения проблемы с кастомными документами мы в первую очередь сделали интерфейс, в котором продакт-менеджер может залить новую версию текста через специальную форму. Изменения сразу попадают в интерфейс переводчиков, который они используют для переводов и основного сайта, и приложения. –Таким образом мы централизовали работу переводчиков и уменьшили количество кастомных сущностей. В этом же интерфейсе можно смотреть и прогресс по всем языкам для каждого приложения. Более того, все переведённые тексты тоже автоматически попадают в этот интерфейс. Больше не нужно метаться по большому документу в поисках нужной локализации, и количество ошибок из-за пресловутого человеческого фактора заметно уменьшилось. Это был первый этап автоматизации. Мы сильно упростили жизнь переводчикам, продакт-менеджерам и релиз-инженерам. Но нам всё ещё приходилось копировать все тексты в iTunes Connect.   В один момент на рынке замаячил новый инструмент, позиционируемый как упрощающий жизнь разработчикам iOS-приложений, – fastlane. Мы сразу же начали его тестировать. Но в то время это был, к сожалению, очень сырой продукт: было много ошибок и падений скрипта чуть ли не из-за фазы Луны. Но тем не менее он открывал много возможностей. Если вкратце, то fastlane потенциально позволяет автоматизировать практически всю работу от компиляции до релиза приложения конечным пользователям: Появление этого инструмента было тем более актуально, что у нас на тот момент уже были мысли написать нечто похожее самостоятельно. Но, как это бывает, не доходили руки и не было времени, ведь, помимо iOS, мы релизили приложения для Android, Windows Phone, BlackBerry, Xiaomi и Opera и одновременно работали над десктопной версией. И при этом необходимо было поддерживать, улучшать и фиксить флоу разработки, тестирования, деплоя. Но мы начали делать шаги в этом направлении. Первым делом мы научили наш интерфейс выгружать все локализации текстовых ассетов, разложенных по папкам и файлам, единым архивом, который может использовать fastlane. Дальше был написан скрипт, который подготавливал файловую структуру, инициализировал Deliver (компонент fastlane)-репозиторий, скачивал из нашего интерфейса все локализации, помещал их куда следует и инициировал загрузку в iTunes Connect через тот же Deliver. Этот скрипт мог использовать любой продакт-менеджер или тестировщик, а не только релиз-инженер. Но самое большое преимущество было в том, что с помощью этого скрипта мы сократили время подготовки стора с получаса–часа до тридцати секунд–минуты. Более того, это позволило свести ошибки из-за человеческого фактора к абсолютному минимуму.  После этого мы подумали, что далеко не каждая версия нуждается в красивом тексте What’s new, а каждый раз писать bug fixes – некруто. Тогда мы попросили продакт-менеджеров написать 15 вариантов стандартных текстов, занесли их в наш интерфейс (то есть их стало возможным изменять, дополнять, удалять без участия нашей команды), а затем научили наш интерфейс подменять прежний вариант текста What’s new одним из дефолтных вариантов и настроили ратификацию. Теперь, если у нас не какой-то очень большой релиз с новыми фичами и масштабными изменениями, мы просто используем один из дефолтных ранее переведённых текстов. По статистике, всего один из десяти–пятнадцати релизов требует большого красивого текста. Для других случаев использование дефолтного варианта (казалось бы, банальщина) значительно экономит время и силы всех участников процесса. Также стоит отметить, что у Apple есть компонент TestFlight. Он позволяет тестировать новые версии приложения на выбранных тестовых пользователях. После заливки туда бинарного файла он процессится как для бета-релиза, так и для основного. Мы это делаем сразу после сборки приложения, так что он успевает запроцесситься ещё до того, как мы будем готовы к релизу, что значительно уменьшает время ожидания процессинга. То есть мы экономим ещё полчаса–час.   На следующем этапе мы решили устранить проблему неудобного и долгого мониторинга прогресса. К этому моменту Apple значительно ускорила процесс ревью: если раньше ожидание ревью занимало около пяти дней, то теперь это стало занимать всего пару дней. Самый маленький период ожидания на нашей памяти – чуть менее суток (около двадцати часов). На тот момент уже был написан кастомный скрипт, который ходил курлом в iTunes Connect и мониторил статусы приложений. Но из-за устройства сайта и необходимости смены активной команды разработчиков (у нас же много приложений) в iTunes Connect, а также по причине периодических изменений сайта сломаться могло что угодно и когда угодно. Собственно, что и происходило из раза в раз. В общем, этот вариант нам не подошёл из-за отсутствия времени и рук для модернизации, фиксов и поддержки. Тогда решено было воспользоваться всё тем же волшебным fastlane. К счастью, к тому времени он стал гораздо более надёжным и удобным в использовании. Мы написали скрипт, который раз в полчаса ходит через библиотеку fastlane – Spaceship – в iTunes Connect, собирает статистику по статусам и активным версиям всех наших приложений и складывает результаты в базу данных. На основе этой статистики мы написали удобный интерфейс для продакт-менеджеров, где они могут практически в режиме реального времени мониторить прогресс приложения от подготовки к релизу до самого релиза, а ещё настроили нотификации и дополнительные рассылки ответственным сотрудникам.  Дополнительно мы сделали стикеры на странице со списком активных билдов с индикаторами жизненного цикла релиза:  Помимо очевидного удобства для всех участников процессов разработки и тестирования, это позволило другим командам получать точные даты смены статусов, релизов, проблем и т. д. Также упростился поиск причин и исследование проблем для абсолютно всех команд Badoo. Итак, теперь на одной странице интерфейса любой желающий может видеть текущий прогресс в релизе абсолютно всех наших приложений и полную историю всех релизов с начала мониторинга статусов и версий. И всё это с точными датами изменений статусов, версий и т. д. В дальнейшем это позволит нам выгружать подробную статистику: как изменилось время полного цикла релиза за месяц или год, например. И, конечно, делать красивые графики со временем, количеством, частотой – кто же не любит графики!  Мы проделали большую работу. Но осталось несколько вещей, которые активно разрабатываются сейчас и есть в планах на ближайшее будущее. Во-первых, хочется сделать удобный интерфейс для заливки локализованных скриншотов продакт-менеджерами. Из-за особенностей таких ассетов и человеческого фактора это очень сложно автоматизировать полностью. К счастью, скриншоты обновляются крайне редко. Во-вторых, хочется привести весь этот процесс от запуска скрипта релиз-инженером или продакт-менеджером к одной-единственной кнопке в интерфейсе любым участвующем в релизе участником процесса, которым так или иначе ежедневно пользуются все QA и продакт-менеджеры. Один клик – и всё готово к релизу! Лепота! Sr Release Engineer