Что такое непрерывная интеграция?

1,00
р.
Проблема: Последнее вливание ветки разработчика в ветку develop занял три дня, при этом пришлось разрулить порядка 100 конфликтов.
Условия: В динамическом проекте 3 разработчика и мы работаем по Git Flow. Разработчики создают свою ветку от ветки develop, а через некоторое время (от 3 дней до 3 недель, в зависимости от сложности задачи) вливают в неё изменения. Возможны глобальные и перекрестные изменения.
Задача: минимизировать затраты времени на интеграцию изменений разработчиков в ветку develop.
Из опробованных средств было соглашение о стремлении к частому вливанию изменений в develop. Это, определенно, не помогло. По идее сервер непрерывной интеграции должен решать эту проблему, но я не могу понять каким образом. В данный момент у нас есть TeamCity, который я развернул для того чтобы он запускал тесты при каждом коммите. И я полагаю что всё-таки не это является основной обязанностью сервера непрерывной интеграции :)
Если непрерывная интеграция подразумевает постоянный merge изменений в develop, то мне становится недоступной ситуация когда я могу в отдельной ветке поломать часть функционала, заменив в течении недели на новый. Если я сливаю изменения с develop моя текущая работа влияет на работу другого разработчика (или это даже плюс?). Но даже если делать вливание в develop только когда все тесты проходят, не думаю что это решит проблему: достаточно крупное изменение будет означать может только один коммит в пару дней (upd поправка: так может случиться с учетом если примем решение коммитить только валидный код, а сложное изменение ломает функционал надолго).
Я хочу услышать как в ваших проектах организована работа с сервером непрерывной интеграции. Как вы бы организовали нашу работу исходя из текущих условий?

Ответ
Непрерывная Интеграция - это процесс объединения копий нескольких разработчиков в общую ветвь несколько раз в день.
Continuous integration Trunk (software)
Хотя распределенные системы контроля версий позволяют не иметь центрального репозитория, именно из-за наличия общих ветвей, объединяющих изменения, основной компонент инфраструктуры непрерывной интеграции это прежде всего центральный репозиторий, развернутый на сервере (Github, Bitbucket, Gitlab).
В дополнение к этому, часто применяются серверы Continuous Integration, основная функция которых - осуществлять сборку и проверку проекта, например TeamCity.
Сервер Continuous Integration никак не поможет с техническими конфликтами системы контроля версий, но он может выявить часть логических конфликтов, например - некомпилирующийся проект, или поломанный тест даже после бесконфликтного с точки зрения системы контроля версий слияния.
В некоторый случаях сервер CI может выявить конфликты слияния, например - с помощью функции Automatic Merge в TeamCity (начиная с версии 8.1).
Незаменимая вещь - отчет сборке, включая результаты автоматических тестов на каждую фиксацию изменения в Trunk, а так же в других опубликованных ветвях. Это позволяет увидеть, если кто-то влил изменения, ломающие тест.
У нас собираются все опубликованные feature-ветви, поэтому можно даже отследить, как поломанный неделю назад в чьей-то ветке тест мигрировал в trunk, и попросить этого разработчика срочно пофиксить тест.
Проблемы объединения изменений возникают, когда разработчик слишком долго не объединял изменения, и сильно "разломал" свою ветвь. При этом проблема больше не в том, что разработчик не публиковал изменения, которые еще не готовы, а в том, что он не "подливал" себе чужие изменения. Не случайно идеология Git - Pull, don't push. Тех, кто делает только push и редко делает pull даже называли "пушистиками" в русском сообществе программистов.
Небольшой пример - я взялся рефакторить некоторый класс A, удалил из него публичный метод GetX, и заменил в 10 местах использование этого метода на другой, GetY.
Предположим, что никто из разработчиков в своих ветвях не пытается вносить изменения в класс A. Это хорошо, потому что даже если мои изменения затронули 10 мест по всему решению, Git имеет шанс разрулить конфликты этих 10 файлов. Кто-то редактировал некоторые из этих файлов, но не трогал ту строку, где вы скоро замените GetX на GetY.
Зато один из разработчиков привнес новое использование публичного метода GetX, потому что он не знал, что этого метода уже как бы нет, и уже интегрировал это изменение в Trunk. Это уже настоящий конфликт, который Git никогда не сможет разрулить. Мало того - Git может и не сообщить об этом логическом конфликте, потому что с точки зрения контроля версий файлов конфликта нет! Этот конфликт обнаружится на этапе компиляции, а некоторые другие логические конфликты и на этапе компиляции не обнаруживаются, и здесь становится ясна роль сервера CI, который выдаст отчет о тестах.
Так вот - если вы подливаете себе новые изменения других разработчиков часто, то вы тут же обнаружите этот конфликт, и тут же в своей ветке замените этот GetX на GetY, поговорите с другими, чтобы они по возможности не делали этого, и все окей - продолжайте дальше "ломать".
Еще один момент - компилируете ли вы перед тем, как сделать merge своей ветви в trunk? А что, сделали pull прямо перед push, и все окей, гит доволен. Все сделали pull, и все ругаются, что у них не собирается проект.
Вот для этого и нужен сервер Continuous Integration, в который вы можете опубликовать свою ветвь, сделать pull request, увидеть что сборка прошла, потом уже делаете merge.
У нас TeamCity шлет письма всем разработчикам, если не прошел билд, или упали тесты. Это очень помогает: сам не заметишь - другие тут же сообщат. Поэтому публикуйте свои ветви, и настройте TeamCity чтобы он собирал буквально все, и гонял по возможности тесты. У нас на каждый коммит гоняются только некоторый набор тестов, так, чтобы билд укладывался в 7 минут примерно. Ночью на транке тестируется билд с полным набором тестов, включая базу данных - это несколько часов.
Есть и несколько других методов, как можно уменьшить количество конфликтов.
Во-первых, постарайтесь разбить задачу на мелкие шаги, которые меняют решение поэтапно. Иногда изменение реализации можно сделать отдельно от изменения API. В первую очередь идут изменения, которые затрагивают API классов, потому что из этих изменений возникает большинство конфликтов. Предупредите других о таких изменениях во всеуслышание, согласуйте и как можно быстрее добейтесь объединения таких изменений, потом убедитесь, что все сделали pull этих ваших изменений.
Здесь помогает утренний скрам, так как другие могут вовремя узнать о том, что вы планируете делать, и посоветовать, как обеспечить "бесконфликтность".
Иногда можно реализовать изменение так, что новая фича, даже будучи не до конца реализованной, не поломает приложения. Например - у нас с десяток интеграций с однотипными партнерами, и добавление партнера - это два-четыре месяца. Никто не ныряет в бранч - фича просто присутствует в коде в отключенном на уровне конфигурации режиме. Первый этап, который подразумевает некоторый рефакторинг API, добавление какого-то базового класса, каких-то значений в справочники - производится по возможности быстро, и интегрируется в trunk, возможно это даже будет некий аврал, но такие вещи делаются задолго до следующего релиза. После этого можно перейти к постоянной интеграции, и спокойно "пилить" хоть год.
Другой вариант - если фича, ради которой разработчики уходят в свой бранч на неделю, затрагивает публичные интерфейсы существующих классов, то можно выделить задачу по изменению API в отдельный бранч, интегрировать его часто, и в основном бранче фичи подливать себе эти изменения так же, как его подливают себе остальные. Хорошо что Git позволяет практически мгновенно переключаться между ветвями.
У нас предусмотрены ветки обслуживания релизов. Например - все основные изменения интегрируются в транк часто, а в ветку релиза ничего никогда не интегрируется. Там делаются только небольшие, точечные изменения, направленные на устранение критических ошибок. Никакого форматирования кода, никакого рефакторинга. Все эти изменения подливаются в trunk сразу же, и уже там находится лучшее решение, исправляющее ошибку более обстоятельно для следующего релиза. Это позволяет избежать конфликтов интеграции этих ветвей практически полностью.
Естественно, есть и неотъемлемые свойства самого проекта, которые могут косвенно влиять на количество конфликтов - это принципы SOLID, о которых уже упомянули в других ответах, при нарушении которых приходится делать изменения сразу в нескольких местах.