Проект ЦИТадель
В подразделе собрана большая библиотека материалов о методологиях — от экстремального программирования до управления рисками. Эта статья посвящена их общей материальной основе: инженерному конвейеру, то есть автоматизированному пути изменения от коммита до продакшена. Непрерывная интеграция, ревью, сборка артефактов, стратегии выкатки и откаты образуют не набор разрозненных «инструментов DevOps», а единую инженерную дисциплину. Её принципы устойчивее названий конкретных систем сборки. Центральный тезис статьи подтверждается отраслевыми исследованиями, хотя противоречит распространённому представлению: скорость и надёжность поставки не противоречат друг другу. Команды, которые выпускают изменения чаще, обычно допускают меньше сбоев; далее разберём механизм этой связи.
Основная проблема коллективной разработки — поздняя интеграция. Ветки, которые развивались независимо месяцами, объединяются в конце работы, и тогда обнаруживаются конфликты и несовместимые предположения; стабилизация занимает недели. Экстремальное программирование сформулировало общий принцип решения этой проблемы: если операция болезненна, выполняйте её чаще. Ежедневная интеграция уменьшает объём каждого объединения, а регулярный выпуск постепенно превращает релиз из отдельного организационного события в обычную операцию. Механизм основан на маленьких партиях: небольшое изменение легче проверить, понять при сбое и откатить. Конвейер позволяет таким партиям двигаться часто, безопасно и без чрезвычайных усилий. Для руководителя важно и другое: продукт конвейера — не сами развёртывания, а обратная связь. Каждый прогон показывает, решает ли изменение поставленную задачу и не нарушает ли остальную систему. Поэтому время цикла определяет, насколько быстро команда узнаёт о своих ошибках и учится на них.
CI — практика, при которой каждое изменение автоматически проходит одни и те же этапы контроля: сборку, статические проверки и тесты. Работающий CI обладает тремя свойствами:
Поверх технических требований действует организационное правило: сломанная основная ветка — это стоп-линия. Красную сборку необходимо немедленно исправить или откатить; остальные задачи могут подождать. Если основная ветка остаётся красной неделями, конвейер перестаёт выполнять свою функцию. Принцип заимствован из производственной системы Тойоты: любой сотрудник может остановить линию с помощью андон-шнура, заметив дефект. Такой механизм работает только там, где сообщать о проблеме безопасно. Если остановившего сборку наказывают, сотрудники перестают поднимать сигнал, и дефекты проходят дальше. Сходный механизм разобран в статье «Разработка с ИИ-ассистентами»: оценивать следует сокрытие проблемы, а не её обнаружение.
Ревью кода — основной дорогой человеческий этап контроля в рассматриваемом конвейере, поэтому он особенно подвержен перегрузке. Его эффективность поддерживают следующие правила:
Исследования поставки, рассматриваемые также в разделе 6, показывают: внешние согласования изменений — комитеты и подписи людей вне команды — не снижают долю неудачных изменений, но заметно увеличивают время цикла. Сотруднику без контекста трудно обнаружить дефект, но ожидание его решения задерживает партию и способствует её укрупнению. Более эффективная замена — ревью внутри команды в сочетании с автоматическими проверками требований, ради которых когда-то ввели ручное согласование. Полезно рассмотреть каждый такой этап, выяснить, после какого инцидента он появился, и по возможности заменить его автоматической проверкой. Она воспроизводимо обнаружит соответствующую проблему и не задержит остальные изменения.
Статья «Разработка с ИИ-ассистентами» объясняет, почему нагрузка на этот этап растёт: написание кода дешевеет, а проверка — нет, поэтому ревью становится узким местом конвейера. Если команда не ограничивает размер диффа, не устанавливает время ответа и не автоматизирует формальные проверки, очередь на слияние возникает именно там, где ожидалось ускорение.
Между интеграцией и выкаткой действует часто нарушаемый принцип: артефакт собирается один раз и продвигается по средам неизменным. Если для стенда, приёмки и продакшена одни и те же исходники собираются заново, получаются три разных артефакта: могут измениться версии зависимостей, компилятор или другие условия сборки. В результате к пользователям попадает не тот объект, который прошёл проверку. В контейнерной среде (см. статью «Контейнеры») CI собирает образ, присваивает ему неизменяемую версию, после чего этот же образ проходит все стенды и поступает в продакшен. Среды различаются только конфигурацией, передаваемой снаружи, в соответствии с двенадцатифакторным подходом. Необходимо также контролировать происхождение артефакта: фиксировать зависимости, проверять известные уязвимости в CI, а в зрелых конвейерах — подписывать артефакты и формировать перечень их состава (SBOM). Риски внешних зависимостей подробнее рассмотрены в статье «Современный JavaScript и TypeScript». Тогда на вопрос, что именно работает в продакшене и из чего это собрано, можно получить машинно проверяемый ответ.
В зрелом конвейере различаются деплой, при котором код доставляется на серверы, и релиз, при котором новое поведение становится доступно пользователям. Разделение этих событий позволяет управлять риском:
Особого подхода требуют миграции данных, которые часто мешают откату. Метод «расширяй и сжимай» (expand/contract) делит изменение на этапы. Сначала новая колонка или таблица добавляется без удаления старой структуры, поэтому прежний код продолжает работать. Затем выкатывается код, который записывает новые изменения в обе структуры, а существующие данные переносятся идемпотентным backfill небольшими партиями с контролем прогресса. После проверки полноты и равенства данных чтение переключают на новую структуру; запись в старую прекращают лишь после периода совместимости. Старую структуру удаляют отдельным изменением, когда к ней уже не обращается ни одна поддерживаемая версия и откат больше не требуется. Это те же принципы терпимого читателя и эволюции без нарушения совместимости, которые рассмотрены в статье «JSON и дизайн API», но применённые к схеме данных.
Теперь можно объяснить тезис из введения. Многолетняя программа DORA и другие отраслевые исследования оценивают поставку по четырём показателям: частоте развёртываний, времени от коммита до продакшена, доле неудачных изменений и времени восстановления. Команды с лучшими первыми двумя показателями обычно показывают хорошие результаты и по вторым двум. Причина заключается в размере партий: частые выпуски состоят из небольших изменений, которые легче проверить (разделы 2–3), локализовать при сбое и откатить (раздел 5). Редкие выпуски объединяют много изменений, усложняя каждую из этих операций. Кроме того, редко используемый процесс выкатки теряет надёжность, как и восстановление, которое не репетируют (см. статью «Непрерывность и восстановление»). Выкатка и откат должны стать обычными операциями, а для этого их необходимо выполнять регулярно. Поэтому политика «выпускать реже ради стабильности» ухудшает и скорость, и надёжность; вместо ограничения релизов требуется развивать конвейер.
Зрелый конвейер обладает ещё тремя важными свойствами. Во-первых, конвейер — единственный путь в продакшен. Ручная выкатка в обход установленного процесса создаёт две версии истины: после неё нельзя достоверно определить, что работает в продакшене и откуда это появилось. Воспроизводимость, аудит и надёжный откат возможны только для изменений, прошедших через конвейер. Обход допустим лишь в порядке аварийного доступа break-glass, описанного в статье «Инфраструктура как код»: его использование должно вызывать сигнал, а внесённое изменение необходимо затем провести через обычный процесс. Во-вторых, конвейер описывается кодом в репозитории: стадии, условия контроля и окружения проходят ревью и версионируются вместе с остальной системой. В-третьих, конвейер относится к самым привилегированным системам компании: он одновременно имеет доступ к исходному коду, секретам и продакшену. Его компрометация ставит под угрозу всё, что он собирает и куда доставляет артефакты. Такие атаки относятся к угрозам цепочке поставок ПО. Поэтому секреты следует хранить в специализированной системе и выдавать на время конкретной задачи, права каждой стадии ограничивать необходимым минимумом, сторонние компоненты и образы фиксировать версиями, а журнал конвейера включать в систему наблюдаемости. Как и в статье «Облака на практике», здесь действует правило: ответственность за эту часть системы нельзя передать поставщику хостинга.
Из этих свойств следует организационный вывод: конвейер — продукт со своим владельцем, а не побочная задача отдельного энтузиаста. Если его поддержка зависит от одного сотрудника и конкурирует с его основной работой, качество конвейера постепенно снижается, а уход этого сотрудника создаёт серьёзный риск. В зрелой организации путь в продакшен имеет владельца, бюджет и собственные показатели: время цикла, стабильность контроля и долю ложных срабатываний. Пользователями такого продукта выступают все инженерные команды. Его владелец определяет и приоритет работ по устранению мигающих тестов или ускорению сборки на основании этих показателей.
Всё существенное воспроизводится локально, без корпоративной системы сборки.
Задание 1. Аудит пирамиды. Для рабочего или учебного проекта измерьте продолжительность полной проверки, число тестов на каждом уровне пирамиды и вклад каждого уровня в общее время. Предложите три изменения, которые могут сократить цикл вдвое: например, кэширование зависимостей, параллельное выполнение или перенос части сквозных проверок на более низкий уровень.
Задание 2. Контроль одним скриптом. Объедините проверку формата, линтер, тесты и сборку контейнерного образа с меткой из хеша коммита в одном исполняемом файле. Обеспечьте три свойства из раздела 2: идемпотентность, работу в чистом контейнере и остановку при первом дефекте. Проверьте воспроизводимость на другой машине или в свежем образе. Подключите скрипт как локальный хук перед отправкой изменений; если используете CI-хостинг, запускайте там тот же файл. Логика конвейера должна находиться в проверяемом скрипте, а не только в настройках веб-интерфейса.
Задание 3. Продвижение артефакта. Проведите образ из задания 2 через две среды: «стенд» и «продакшен». Создайте два compose-окружения, которые различаются только внешней конфигурацией. Убедитесь, что в обоих используется один и тот же образ с неизменяемой меткой, и зафиксируйте этапы, на которых раньше выполнялась повторная сборка.
Задание 4. Канарейка с метриками. На стенде из практикума статьи «Наблюдаемость» (Prometheus и два сервиса) запустите за балансировщиком две версии приложения с распределением трафика 90/10. В новую версию внесите контролируемую деградацию: задержку или повышенную долю ошибок. Напишите проверку, которая сравнивает p99 и долю ошибок канареечной и основной групп, после чего принимает решение об остановке или продолжении. Автоматизируйте откат — это составит основу прогрессивной поставки.
Задание 5. Расширяй и сжимай. Переименуйте колонку в работающей базе данных по этапам из раздела 5; для практикума достаточно SQLite. Заранее добавьте строки со старым форматом, включите двойную запись, выполните повторяемый backfill и проверьте, что новая колонка заполнена для всех строк и совпадает со старой. Только после этого переключите чтение, прекратите запись в старую колонку и отдельным изменением удалите её. До фазы удаления прежний код должен продолжать работать, а на каждом этапе необходимо сохранять возможность отката. Сравните результат с одноэтапным выполнением ALTER TABLE и перечислите появившиеся возможности.
Задание 6*. Исследование мигающего теста. Возьмите мигающий тест из своего проекта и установите причину: гонка в тесте, гонка в программе, зависимость от окружения или времени. Исправьте его либо временно поместите в карантин с отдельной задачей. Затем сформулируйте командное правило работы с такими тестами, чтобы они не снижали доверие к системе контроля.