CI (непрерывная интеграция) ценна только тогда, когда проваливается. Когда она проходит успешно, то становится просто оверхедом: того же результата можно было добиться и без CI.
Что такое Continuous Integration?
Разработка ПО следует по цикличному итеративному паттерну. Разработчики вносят изменения, коммитят их в систему управления версиями, развёртывают их для пользователей и повторяют этот процесс. Этап continuous integration (CI) расположен между коммитами и развёртыванием, это выполнение автоматизированных проверок каждого коммита. Если проверка проходит успешно, мы говорим «CI пройдена», после чего изменение развёртывается. Если проверка проваливается, мы говорим «CI не пройдена», и изменение не развёртывается.

Если вы опытный разработчик, то, возможно, думаете: «Ну это само собой!». Чтобы по-настоящему осознать предназначение CI, нужно посмотреть, что происходит с CI и без неё.
Цикл обратной связи
Хотя я часто слышу «можно ведь просто не быть тупым», надо быть реалистом: мы, разработчики, совершаем ошибки; более того, чем мы продуктивнее, тем их больше.
Что происходит, когда мы делаем ошибки? Последствия могут находиться в спектре от «теперь код неправильно отформатирован» до «платежи не проходят, и мы теряем миллионы в час».
Без CI мы можем найти ошибки только после развёртывания, когда с ними сталкиваются пользователи или коллеги. Тогда мы откатываемся к предыдущей версии, устраняем проблему и пробуем снова.

Обратите внимание, что ошибка становится заметной только после развёртывания, и её могут заметить спустя произвольное количество времени (если её вообще заметят!), когда она уже нанесла урон. Из-за этого такой цикл обратной связи оказывается долгим, ручным и опасным.
Обнаружение проблем на ранних этапах
Никакие проверки не способны выловить все ошибки, но они определённо могут обнаружить их часть, и, как оказывается, это тоже ценно.
«Тестирование программ может показать наличие ошибок, но никогда не покажет их отсутствие!» ― Эдсгер Дейкстра.
Любая ошибка, отловленная CI — это ошибка, не дошедшая до продакшена.
Давайте посмотрим, что происходит, когда CI проваливается из-за ошибки:

В этом случае процесс прерывается (и начинается заново) до развёртывания. Благодаря этому цикл обратной связи становится короче, более автоматизированным и менее опасным.
Но стоит помнить, что это помогает, только если CI действительно может отловить ошибку. Эту проблему невозможно решить. В случае, когда CI не может обнаружить ошибку, мы возвращаемся к сценарию без CI.
Вероятно, на практике вам потребуются чуть более строгие проверки, чем вы думаете, однако совершенно точно существует и ситуация, когда «CI слишком много».
CI — это страховочный трос
Если сравнить случай с ошибкой с CI и без неё, то можно увидеть, как CI меняет исход:

Здесь мы чётко видим, в чём заключается ценность CI: она предотвращает плохой исход (возникновение ошибки), отлавливая ошибку на раннем этапе.
Слишком много CI
Если CI — это хорошо, то чем больше CI, тем лучше, ведь так? Нет, не совсем. Чтобы понять, почему это так, нужно посмотреть, что происходит, когда ошибки не совершаются:

Обратите внимание, что конечный результат в обоих случаях одинаков: изменение успешно развёртывается. Единственное различие заключается в том, что в случае с CI перед развёртыванием нам приходится ждать, пока выполнится и успешно пройдёт CI.
Это значит, что в случае отсутствия ошибок CI оказывается просто лишним этапом, который усложняет нам жизнь и замедляет работу, не давая никакой ценности.
Неисправные CI
Весь смысл применения CI в том, что мы ожидаем ошибок разработчиков, поэтому при подготовке самой CI мы не можем предполагать, что они не будут совершать ошибок. Как не можем мы и предполагать, что разработчики самой системы CI не могут совершать ошибок.
Может возникнуть пугающая и очень распространённая ситуация: проваливающуюся CI можно пройти, просто запустив её повторно. Мы называем это нестабильной (flaky) CI.
Нестабильная CI крайне неприятна тем, что проваливание CI больше не указывает надёжно на обнаружение ошибки. И вдвойне неприятна она нем, что неустранима (теоретически); иногда машины просто ломаются.
К счастью, нестабильность можно обнаружить: при провале выполнения CI мы можем запустить её повторно. Если во второй раз она проходит, то мы будем уверены, что она нестабильна. Если она проваливается во второй раз, то мы могли отловить реальную ошибку (но также это может и значить, что повторно проявилась нестабильность).
Неисправные CI — это реальная и важная проблема, которую я люблю решать, но это уже тема для отдельной статьи.
Ценность CI
Снова вернёмся к нашим четырём сценариям:

Обратите внимание, что в случаях без ошибки прохождение или несуществование CI никак не влияет на результат. Разница есть только в случае с ошибкой, где провал CI предотвращает плохой результат. Это значит, что единственный ценный исход CI — это её провал.
Что подразумевается под «провалом»
К сожалению, ценный исход CI называется «провалом», как будто это что-то плохое. Цвета, используемые для обозначения результатов CI, тоже выбраны не совсем неправильно. Вот, как это обычно выглядит:

Более того, ценный результат «провал» (Failure) обозначается той же иконкой и цветом, что и наихудший: нестабильность (Flaky).
Я предлагаю использовать такие иконки:

Или даже добавить немного эмодзи, чтобы мы точно понимали, что испытывать в каждом из исходов:

Наверно, вносить это изменение слишком поздно: все уже привыкли, что красный цвет обозначает «требуется действие», но надеюсь, что это позволит вам посмотреть на провалы CI под новым углом.
Заключение
Ценность CI заключается в провалах, а не в успешном прохождении. Нестабильность снижает эту ценность.
Во всех показанных в посте схемах этапы «Работа» и «Коммит» шли до «Выполнения CI». В следующем посте мы поговорим о том, как оптимизировать эту систему благодаря local-first CI.