CI - разрабам!
CI - разрабам!

Все знают аббревиатуру CI/CD. Continuous Integration and Continuous Delivery - Непрерывная интеграция и Непрерывная поставка. Но едва ли можно найти более неправильно понимаемую нашей индустрией идею, чем непрерывная интеграция. Практика, которая была придумана, чтобы её делали разработчики, почему-то превратилась в объект работы девопсов, хотя к культуре DevOps ну вообще никакого отношения, по идее, иметь не должна.

Так что вот вам статья про то, как так вышло, что сейчас под CI понимают что угодно, кроме того, чем она, на самом деле, является.

История треснувшего шаблона.

Лет 5 назад, просматривая второе издание Рефакторинга от Мартина Фаулера, я наткнулся на одну мысль, которая тогда порвала мне шаблон.

Как обычно, начал от Рюрика.
Как обычно, начал от Рюрика.

Там была упомянута интересная проблема рефакторинга. Оказалось, масштабные рефакторинги по всей кодовой базе часто провоцируют сложные мёрдж-конфликты. Из-за этих трудностей команды порой даже совсем отказываются от практики регулярного рефакторинга.

И мне подобное знакомо - у меня однажды был конфликт на эту тему, когда мой ПР с большим рефакторингом, залетел в мастер на день раньше, чем большой ПР коллеги. Товарищ, столкнувшись с огромной кучей конфликтов, полыхал мне потом в личку весь день. И был прав, ведь я, не проконсультировавшись с ним, откинул его прогресс по работе на целый день назад.Он тогда так и сказал: "Давайте больше не делать таких больших рефакторингов". Классная идея.

В книжке Мартин, как настоящий экстремальщик, справедливо пишет, что это, на самом деле, не проблема рефакторинга, а проблема подхода к работе с системой контроля версий. Этой проблемы, дескать, бы не было, если б вместо долгоживущих веток использовалась непрерывная интеграция, которая именно для этого и была придумана в Экстремальном программировании (XP).

Так-так, подумал я тогда, а при чем тут Непрерывная интеграция?

Вот тут-то у меня шаблон и порвался. Выяснилось, что Непрерывная интеграция - это вообще не про джобы в дженкинсе и гитлабе. После короткого поиска первоисточников, оказалось, что непрерывная интеграция была впервые описана Кентом Беком в 1999 в его книге Extreme Programming Explained.

Иллюстрация практики непрерывной интеграции. Источник.
Иллюстрация практики непрерывной интеграции. Источник.

Согласно книге, непрерывная интеграция - это практика, где все разработчики, во избежание конфликтов и коллизий, сливают в одну ветку все свои изменения как можно чаще. В идеале каждые пару часов.

Continuous Integration.

No code sits unintegrated for more than a couple of hours. At the end of every development episode, the code is integrated with the latest release and all the tests must run at 100%.

Extreme Programming Explained. Kent Beck. 1999

Для душнил

На самом деле, я тут немного утаил один незначительный момент: Викпедия не врет. Действительно, первое использование словосочетания Continuous Integration все же остается за Грейди Бучем. Он упомянул эту практику в 1991 году в книжке Object Oriented Design.

Я в свое время насилу откопал оригинал первого издания 1991 года, чтобы увидеть в итоге, что он вложил в это словосочетание совсем другой смысл. Грейди имел в виду регулярную интеграцию частей системы на стендах для раннего обнаружения проблем межмодульного взаимодействия.

В контексте этой статьи интерес этот факт представляет лишь исторический. Вариант использования этого словосочетания от Грейди Буча так и потонул в истории, уступив место XP-шной практике, которую популяризировали главные авторитеты индустрии, состоявшие тогда по большей части из экстремальщиков.

Сравните иллюстрацию с сайта Мартина Фаулера с тем, что выдаёт Гугл, и вам станет понятно, про что эта статья. Особенно смешно выглядит первая картинка: точно такую же выдаст Гугл на запрос Agile, который является не меньшим объектом карго-культа, чем предмет нашей статьи.
Сравните иллюстрацию с сайта Мартина Фаулера с тем, что выдаёт Гугл, и вам станет понятно, про что эта статья. Особенно смешно выглядит первая картинка: точно такую же выдаст Гугл на запрос Agile, который является не меньшим объектом карго-культа, чем предмет нашей статьи.

Идея непрерывной интеграции, на самом деле, очень натуральная для Экстремального программирования. Авторы XP пытались сделать все практики очень частыми (в сравнении с распространенным тогда Водопадом) или вовсе непрерывными:

  • вместо долгой фазы дизайна системы - частые и короткие эпизоды рефакторинга,

  • вместо многомесячной фазы ручного тестирования - регулярно запускаемые быстрые автотесты,

  • вместо периодического код-ревью - непрерывное парное программирование.

  • вместо одного релиза в год-полтора - короткие итерации с поставками раз в 2 недели.

Вот в этот же список попала и непрерывная интеграция (CI) с ежедневными слияниями изменений в главной ветке.

Итого: согласно определению, непрерывная интеграция - это когда ВСЕ разработчики сливают свои изменения хотя бы раз в день.

Но после того,как паззл сошёлся, название "непрерывная интеграция" обрело смысл, а практика - пользу, мне еще предстояло ответить на очень много вопросов.

Очень много вопросов (три).

Вопрос 1. При чём тут джобы, билды и тесты?

Сама по себе непрерывная интеграция в самой простой формулировке может отловить только конфликты слияния. Но, вообще говоря, надо бы еще вылавливать и иные виды конфликтов.

  1. Например, коллизии имён. Это когда один разработчик поменял название функции, а второй добавил её вызов. Конфликта не будет, но скомпилироваться оно уже не сможет. Чтобы такого не было, перед пушем надо спуллиться и скомпилировать репозиторий.

    Пример коллизии имён. Разработчик 1 поменял имя на новое. Разработчик 2 использовал старое имя. При мерже конфликта не будет, но не скомпилится.
    Пример коллизии имён. Разработчик 1 поменял имя на новое. Разработчик 2 использовал старое имя. При мерже конфликта не будет, но не скомпилится.
  2. Другой пример конфликта - когда решение одного разработчика ломает всю логику реализации второму. Или язык интерпретируемый и компиляция никак не поможет. Такое вообще можно отловить только запустив тесты. Получается, перед тем, как запушить, разработчик должен спуллить все изменения в ветке, собрать, прогнать тесты и только потом можно уже пушнуть.

  3. Но есть еще и третий вариант конфликта - локальные особенности рабочих машин разработчиков: у одного есть нужный файл, у другого - нет. У одного установлено что-то, а у другого - нет. Решается сборкой и тестами каждого коммита на отдельной машине.

"У меня такая же нога и ничего не болит"
"У меня такая же нога и ничего не болит"

Довольно скоро вот эта отдельная машина превратилась в CI-серверы, на которых на каждый коммит в мастер происходит пулл, билд и тест. И вот уже на этой теме и выросли все эти Дженкинсы и Гитлабы, джобы в которых и стали принимать за настоящий CI, позабыв об оригинальной идее.

Вопрос 2. Как делать код-ревью, когда вы делаете ПР раз в 2 часа? Да даже раз в день.

Тут есть три ответа:

  1. Непрерывная интеграция появилась в середине девяностых задолго до повсеместного распространения практики код-ревью методом пулл-реквестов. В экстремальном программировании код-ревью проводилось посредством парного программирования, поэтому тогда такой вопрос особо не стоял.

  2. Но сейчас стоит, поэтому второй ответ: мне известен пример гугла, который побуждает своих разработчиков сливать ветки раз в день или два, разбивая работу на маленькие мёрж-реквесты. Это работает, потому что на маленькие ПР времени надо меньше, чем на большие. Ну и потому что это гугл, они и за работу без дизайн-доки не сядут - могут себе позволить.

  3. Но третий ответ - самый честный. Код ревью - это хороший способ отловить заметную часть багов, повысить читаемость кода, передать знания и научить крутым штукам. Но, если уж напрямую, самая главная причина популярности код-ревью - это проблема доверия. Например, пулл-реквесты - это механизм, разработанный для обзора изменений в открытых кодовых базах, где степень доверия к контрибьютору невысокая. И я мог бы в стиле аджилистов сказать, что, если вы не в опенсорсе, то просто решайте проблему доверия. Но это будет несправедливо по отношению к читателю, поэтому подбираемся к самому главному вопросу.

Вопрос 3. А есть ли в современных условиях смысл в CI?

И тут ответ зависит только от вас и вашей команды.

  • Нет, если вы особо не рефакторите, и у вас нет проблем с конфликтами слияния.

  • Нет, если у вас в команде есть проблемы с доверием, и вы их не решаете по каким-то причинам. Например, вы не доверяете некоторым своим разработчикам, но не можете несколько раз в день смотреть их ПРы или посадить их в пару с более опытными спецами.

  • Да, если у вас происходят регулярные конфликты, которые сильно замедляют работу.

  • Да, если вас больше двух, и вы активно рефакторите кодовую базу.

Но, если ответ положительный, вам для этого все равно потребуется решить проблему доверия одним или несколькими способами из списка:

  • нанимать только тех, кому доверяете;

  • обучать тех, кому не доверяете;

  • сажать тех, кому не доверяете, в пару к тем, кому доверяете;

  • делать частые ПРы и часто их смотреть, как в Гугле;

  • переосмыслить своё недоверие.

Дисциплина непрерывной интеграции.

Уже ясно, что непрерывная интеграция - это не джоба в дженкинсе, а именно практика. Но, на самом деле, непрерывная интеграция - это не только практика частых вливаний изменений в мастер, но дисциплина поддержания мастер-ветки в постоянно рабочем состоянии.

А раз это дисциплина, то это означает, что она имеет очень строгий алгоритм действий:

  1. Готов пушнуть? Посмотри, зеленый ли мастер.

  2. Если последний коммит уронил тесты, а за 5 минут никто ничего не починил - откатывай последний коммит.

  3. Если мастер зеленый, можно тянуть изменения из мастера и гонять тесты.

  4. Если твои тесты красные, почини и вернись на пункт 1.

  5. Если тесты зеленые, пушнул и идёшь смотреть, зеленые ли они на сервере.

  6. Если на сервере тесты красные - откатывай. Если чинить 5 минут, то можешь просто починить. Главное, чтобы мастер снова стал зеленым в кратчайшее время.

  7. Если мастер зеленый, то можешь спокойно работать дальше.

Больше для непрерывной вам ничего не нужно: на эту тему даже есть замечательная статья про то, как делать CI с помощью резиновой утки, колокольчика и старого компа. Никаких девопсов, дженкинсов и чего-либо еще.

Заключение по CI.

Я люблю списочки, поэтому закончу еще одним.

  1. Практика большей частью индустрии вообще не понята, а точнее понята не так. Произошёл типичный пример размывания смысла - испорченный телефон, когда внешний атрибут практики принимается за саму практику. В нашей индустрии такая фигня, честно говоря, вообще повсюду. Это предмет отдельного разговора, но, для протокола, эта статья была не совсем про CI.

  2. Практика в оригинальном виде - это для тру-экстремальщиков. Для тех, у кого есть 100% покрытие, рефакторинг, доверие и парное программирование.

  3. Практика нифига не устарела, до сих пор является дефолтной рекомендацией всяких там State of Devops, Accelerate и общепризнанных Гуру, обязательна к исполнению в айти-гигантах типа гугла.


Меня зовут Саша Раковский. Работаю техлидом в расчетном центре одного из крупнейших банков РФ, где ежедневно проводятся миллионы платежей, а ошибка может стоить банку очень дорого. Законченный фанат экстремального программирования, а значит и DDDTDD, и вот этого всего. Штуки редкие, крутые, так мало кто умеет, для этого я здесь - делюсь опытом. Если стало интересно, добро пожаловать в мой блог.

Комментарии (19)


  1. n0isy
    15.07.2025 00:48

    Три несовместимых вещи: большой рефакторинг + частые мерджи + тесты. На то он и большой рефакторинг, что требует заморозки другого тех долга.

    От начала и до конца рефакторинга система будет находится в неконсистентном состоянии. Практически до последнего коммита


    1. RakovskyAlexander Автор
      15.07.2025 00:48

      "Рефакторинг — это применение небольших шагов, сохраняющих поведение, и внесение больших из­ менений путем связывания воедино последовательности упомянутых малых ша­ гов, сохраняющих поведение. Каждый отдельный рефакторинг сам по себе либо довольно маленький, либо представляет собой комбинацию маленьких шагов.
      В результате, когда я выполняю рефакторинг, мой код не оказывается надолго в неисправном состоянии, что позволяет мне останавливаться в любой момент, даже если я еще не закончил всю намеченную работу полностью.

      Если кто-то говорит, что его код был неработоспособен в течение не- скольких дней во время выполнения рефакторинга — можете быть увере­ ны, что никакого рефакторинга не было."

      "Рефакторинг", 2 издание, 2018 год, Мартин Фаулер


      1. LyuMih
        15.07.2025 00:48

        Попробуйте поднять версию MUI4 на MUI5/6/7 или React 17 на 18/19.

        У компаний уходят на это месяцы и то не факт, что получается с первого раза


        1. RakovskyAlexander Автор
          15.07.2025 00:48

          Есть технологии постепенного внедрения больших изменений. Как человек, который постоянно пользуется ими, могу сказать, что ничего невозможного тут нет. Ими не все пользуются, потому что не все понимают, зачем. Но если понимание зачем есть, то достаточно только чуть-чуть подумать, и сразу придумается решение.


          1. Gorthauer87
            15.07.2025 00:48

            Некоторые изменения по своей сути атомарны, несмотря на то, что объёмные. Их не сделаешь постепенно - это фундаментальная их особенность.

            Можно раздробить изменения до уровня атомарных, но не дальше.


            1. RakovskyAlexander Автор
              15.07.2025 00:48

              Мы часто переоцениваем неделимость изменений :)

              едва ли я смогу вспомнить хоть одно изменение за свою карьеру, которое нельзя было бы разбить на куски, не превышающие один день)


    1. neciro
      15.07.2025 00:48

      Из моего опыта по поводу "большой рефакторинг + частые мерджи + тесты" - часто (ежедневно) вливайте изменения из мастера в вашу ветку и будете спать спокойнее. Но всё равно законченные части работы лучше сливать в мастер и закрывать feature flag'ами. Потому что паралельно может разрабатываться другое "большое изменение" о которым в даже и знать возможно не будете.


      1. RakovskyAlexander Автор
        15.07.2025 00:48

        В точку!


  1. ky0
    15.07.2025 00:48

    Не очень понятен наезд на инженеров эксплуатации в первом абзаце. Ведь если "Непрерывная интеграция - это вообще не про джобы в дженкинсе и гитлабе" - забирайте эту правильную интеграцию себе, а джобы оставьте им, просто у себя в голове называйте как-то иначе - и спите спокойно :) Или всё ради крестового похода за именование?


    1. RakovskyAlexander Автор
      15.07.2025 00:48

      Нет наезда. Есть констатация непонимания сути практики всеми участниками разработки. Кто пишет джобы - второстепенный вопрос. Статья про то, кто делает ci.


    1. ForestDront
      15.07.2025 00:48

      Не-не-не Дэвид Блейн... Пускай эта галиматья остаётся девопсам


  1. ialexander
    15.07.2025 00:48

    Ну то есть вы предлагаете отказаться от практик code review, под предлогом того, что они мешают идее из 90-х, и даже отражают недоверие в команде.

    Что касается другой трактовки CI, в целом это нормально когда идея развивается и видоизменяется со временем, не все из них хороши сразу, некоторые требуют адаптации к реальности.

    PS

    Я не критикую идею постоянных мелких комитетов, которая вполне может работать в маленькой однородной и сильной команде. Но в современных условиях команды часто большие и неоднородные и включают не только опытных разработчиков, знающих код, но и просто начинающих и даже интернов. Парное программирование, когда аллоцируется целый разработчик на интерна, дороговато выходит, дешевле всё-таки code review. Само code review похоже все та же идея парного программирования, адаптированная к реальности и сделанная асинхронной практикой.


    1. RakovskyAlexander Автор
      15.07.2025 00:48

      Как я и написал в статье, практика подходит далеко не всем командам. Использовать или нет - ваше решение)


    1. pipewalker
      15.07.2025 00:48

      > дороговато выходит, дешевле всё-таки code review.
      Обычно так говорят, когда не считают:
      - стоимость задержек и ожиданий
      - стоимость переделок (чем позже, тем дороже)
      - стоимость хорошего код-ревью (не просто поскроллить дифф, а пойти узнать, что за задача, и разобраться, почему она была решена именно так).

      И когда не разбираются в том, как устроено обучение, и почему code reivew в большинстве случаев - не оно :)


      1. RakovskyAlexander Автор
        15.07.2025 00:48

        100%


      1. warkid
        15.07.2025 00:48

        Ну и если уж нуден review - можно его делать post-commit.


        1. RakovskyAlexander Автор
          15.07.2025 00:48

          Так и есть, асинхронный код ревью - тоже вполне себе практика.


  1. Q3_Results
    15.07.2025 00:48

    Хочу сказать Вам большое спасибо за проработанный структурированный материал с исторической базой


    1. RakovskyAlexander Автор
      15.07.2025 00:48

      Спасибо большое за комментарий, это так приятно)