Со временем в софтверной индустрии придумали несколько способов более быстрого и безопасного выпуска качественного кода. Многие основаны на идеях вроде непрерывной интеграции, непрерывной поставки ПО, гибкой методологии разработки, DevOps и разработки через тестирование. Все эти методологии объединяет одно: они позволяют разработчикам быстро выпускать код безопасными, небольшими, последовательными шагами.
Процессы разработки в Facebook органично выросли из этих методологий и включили в себя многие этапы быстрых итераций без жёсткого следования какой-нибудь конкретной из схем. Такой гибкий, прагматичный подход позволил нам успешно выпускать веб- и мобильные продукты по быстрому графику.
В течение многих лет мы обновляли фронтенд Facebook трижды в день, используя простую стратегию веток master и release. Инженеры избирательно выбирали из ветви master изменения в коде, которые прошли ряд автоматизированных тестов, для включения в ветвь release, откуда происходили ежедневные обновления. В целом, таким способом выбиралось от 500 до 700 изменений в день. Раз в неделю мы отрезали новую ветвь release и собирали изменения, которые не отобрались в течение недели.
Такая система неплохо масштабировалась, начиная с нескольких разработчиков в 2007 году до нескольких тысяч сегодня. Хорошая новость в том, что по мере увеличения количества программистов увеличился объём сделанной работы — скорость выпуска кода масштабировалась вместе с размером команды. Однако для ежедневных и еженедельных релизов потребовались определённые усилия, назначение ответственных за релизы разработчиков, вдобавок к имеющимся инструментам и автоматизированным системам. Мы понимали, что накопление всё больших и больших порций кода для выпуска не может продолжаться вечно, поскольку количество разработчиков продолжало расти.
К 2016 году мы пришли к выводу, что модель двух веток с ручным выбором фрагментов достигла предела. В master-ветку вносилось более 1000 изменений в день, а еженедельный пуш достигал 10 000 изменений. Количество усилий для координирования и выпуска такого большого релиза каждую неделю стало нежизнеспособным.
В апреле 2016 года мы решили перевести facebook.com на почти непрерывную систему «пуш из ветки master». В течение следующего года мы постепенно выкатывали её, сначала для половины сотрудников, потом от 0,1% до 1% веб-трафика в продакшне, затем для 10%. Каждое из этих продвижений помогало проверить возможности наших инструментов и процессов работать с более высокой частотой пушей и получать обратную связь из реального мира. Основной целью было убедиться, что новая система улучшает восприятие сайта пользователями — ну, по крайней мере, не портит его. После почти года планирования и разработки в течение трёх дней в апреле 2017 года мы развернули 100% наших продакшн-серверов на запуск кода непосредственно из ветки master.
Непрерывная поставка кода в большом масштабе
Хотя истинно непрерывная система поставки ПО означает, что каждое отдельное изменение быстро уходит в продакшн, скорость разработки в Facebook потребовала создания своей системы, которая пушит тысячи изменений каждые несколько часов. Изменения в этой квази-непрерывной системе поставки ПО обычно маленькие и постепенные, и очень немногие из них реально видимы для конечного пользователя. Каждый релиз выкатывается на 100% серверов продакшна в несколько этапов за несколько часов, так что мы можем остановить пуш, если заметим какие-то проблемы.
Изменения, которые прошли ряд внутренних автоматизированных тестов и попали в ветку master, выкатываются для сотрудников Facebook. На этом этапе предусмотрены оповещения о блокировке пуша, если мы внесли регрессию, а кнопка экстренной остановки быстро останавливает дальнейшее распространение релиза. Если всё в порядке, мы выкатываем изменения для 2% продакшн-серверов, где снова собираем обратный отклик и отслеживаем предупреждения, особенно для крайних случаев, которые при тестировании на собственных сотрудниках могли пройти незамеченными. В конце концов, мы выкатываем изменения для 100% продакшна, а наш инструмент Flytrap собирает отчёты пользователей и информирует о любых аномалиях.
Многие изменения сначала удерживаются в системе Gatekeeper, чтобы выкатывать релизы веб- и мобильной версии независимо от новых функций. Это помогает снизить риск, что какое-то конкретное обновление вызовет проблемы. Если мы находим проблему, то просто отключаем Gatekeeper вместо того, чтобы возвращаться на предыдущую версию или вносить исправления в следующей.
У квази-непрерывного цикла релизов есть некоторые преимущества:
Не нужны заплатки. В системе с тремя пушами в день, если срочно требовалось критическое изменение, которое не вписывалось в график, то приходилось применять заплатку. Эти внеплановые пуши вредны, потому что требуют определённых усилий со стороны разработчиков и могут конфликтовать со следующим плановым пушем. С новой системой в абсолютном большинстве случаев изменения, которым требуется заплатка, просто вносятся в ветку master и накатываются с ближайшим релизом.
Лучшая поддержка глобальной группы разработки. Мы пытались приспособить три ежедневных пуша к графику работы офисов разработки в разных частях света, но даже в таких условиях один еженедельный релиз всё равно требовал внимания всех разработчиков в конкретную дату и время, что не всегда удобно в их часовых поясах. Новая квази-непрерывная система означает, что все разработчики в любых часовых поясах могут писать и поставлять свой код тогда, когда им удобно.
Стимул для создания новых инструментов, автоматизации и процессов, необходимых для масштабирования. Когда мы берёмся за такие проекты, то они как испытание давлением для многих наших групп разработчиков и систем. В результате мы улучшили push-инструменты, инструменты обзора изменений, инфраструктуру тестирования, систему управления мощностями, системы маршрутизации трафика и внесли улучшения во многих других областях. Разработчики всех этих систем хотели, чтобы проект быстрых релизов завершился успехом. В итоге, сделанные улучшения помогут компании лучше подготовиться к будущему росту.
Удобство для пользователей. Если для изучения, как ведёт себя новый код, требуются дни и недели, то разработчик может уже перейти к другой работе. С непрерывной поставкой ПО инженерам не требуется ждать неделю или больше, чтобы получить обратную связь насчёт внесённых изменений. Они быстрее получают информацию, что не работает, и оперативно выкатывают небольшие изменения по мере готовности, а не ожидают следующего большого релиза. С точки зрения инфраструктуры новая система гораздо удобнее при реагировании на редкие события, которые могут повлиять на людей. В конечном счёте, инженеры стали ближе к пользователям сайта, улучшилось и качество разработки, и надёжность продукта.
Применение непрерывной поставки для мобильной платформы
Переход на квази-непрерывную систему в вебе стало возможным частично потому, что мы контролируем весь технологический стек, можем создать или улучшить необходимые инструменты. Поставка на мобильные платформы — более сложная задача, поскольку многие существующие инструменты для разработки и внедрения на мобильной платформе не поддерживают быстрые итерации для непрерывного выпуска ПО.
Facebook постарался улучшить ситуацию в этой области и выпустил ряд свободных инструментов, которые специально заточены на быструю мобильную разработку. Среди них Nuclide, Buck, Phabricator, различные библиотеки для iOS, React Native и Infer. Все вместе эти инструменты для сборки и тестирования позволяют выпускать качественный код, готовый для быстрой поставки на мобильные платформы.
Наш стек непрерывной интеграции состоит из трёх уровней: билды, статический анализ и тестирование.
Как только код поступает из ветви разработки в наши мобильные master-ветви, его сначала собирают для всех продуктов, к которым он может относиться. На мобильной платформе это относится к билдам Facebook, Messenger, Pages Manager, Instagram и другим приложениям при каждом коммите. Мы также собираем несколько вариантов каждого приложения для гарантии, что покрыли все микропроцессорные архитектуры и симуляторы, которые поддерживает эта программа.
Пока поступают билды, мы запускаем линтеры и наш инструмент для статического анализа кода Infer. Это помогает выявить исключения с null-указателем, утечки ресурсов и памяти, неиспользуемые переменные и рискованные системные вызовы, а также пометить проблемы несоответствия правилам программирования Facebook.
Третья параллельная система, мобильное автоматизированное тестирование, содержит тысячи юнит-тестов, интеграционные тесты и неразрывные тесты (end-to-end), которые проводятся с помощью инструментов вроде Robolectric, XCTest, Junit и WebDriver.
Этот набор инструментов для сборки и тестирования не только запускается на каждом коммите, но и несколько раз запускается на протяжении жизненного цикла каждого изменения в коде. Только под Android мы собираем от 50 000 до 60 000 билдов в день.
Используя традиционные методы непрерывной поставки ПО на нашем мобильном стеке, мы перешли от релизов каждые четыре недели к двухнедельному, а затем еженедельному циклу. Сегодня на мобильной платформе мы используем ту же модель ручного отбора изменений, которую раньше использовали в вебе. Хотя мы выкатываем изменения в продакшн только раз в неделю, по-прежнему важно заранее тестировать код в реальных условиях, чтобы инженеры заранее получили отзывы от пользователей. Мы каждый день выпускаем релиз-кандидаты для бета-тестеров, среди которых около 1 миллиона тестеров под Android.
В то время как мы увеличили частоту выпуска релизов, численность наших разработчиков для мобильных платформ увеличилась в 15 раз, а скорость разработки тоже значительно выросла. Несмотря на это, данные с 2012 по 2016 годы показывают, что производительность труда инженеров осталась неизменной под Android и iOS, как по количеству строк кода, так и по количеству пушей. Также и количество критических проблем в мобильных приложениях почти не изменилось, независимо от количества релизов, то есть качество кода не пострадало в результате масштабирования.
Очень приятно работать в области проектирования релизов на фоне таких улучшений в доступных инструментах и методологиях. Я очень горд за группы разработки в Facebook, которые работали сообща, чтобы создать, на мой взгляд, самые продвинутые в мире системы внедрения веб- и мобильных приложений подобного масштаба. Такое было бы невозможно без сильной группы проектирования релизов, которая была на первых ролях в отделе разработки инфраструктуры. Эта группа в Facebook продолжит продвигать инициативы, которые улучшают для разработчиков и пользователей процесс выпуска релизов, и продолжит делиться своим опытом, инструментарием и лучшими практиками.
Автор: m1rko