В этом стриме я хочу поговорить про технологические моменты, связанные с организацией работы над проектом. Независимо от уровня зрелости вашей команды, или даже при полном отсутствии таковой вы скорее всего используете систему контроля версий для хранения и работы над кодом.
Темы, которые планируется затронуть:
- Виды разработки и их особенности
- Версионирование
- Контроль версий (Git)
Существует два устоявшихся термина в отношении работы, связанной с разработкой программного кода:
- Разработка продукта
- Разработка проекта
В АйТи бизнесе продуктом принято называть софт, которая либо разработана на заказ, либо продается в коробочном виде. Этот подход подразумевает разработку и передачу результата разработки конечным пользователям.
Важные аспекты:
- Обратная совместимость;
- Разнородная версионность (у пользователей могут быть разные версии и проблемы);
- Поддержка на уровне версий;
- Релизная система.
Если бизнес разрабатывает софт, который предоставляет какой-то сервис для конечного пользователя, при этом пользователь не получает собственной версии программы, то такой софт принято называть "проектом". А модель бизнеса, по которой предоставляется услуга, называют SAAS.
Важные аспекты:
- Обратная совместимость в момент выпуска новой версии;
- Инкрементальные улучшения;
- Непрерывные улучшения;
- Миграции при изменении мажорных версий;
- Постоянные улучшения на уровне фич, а не релизов.
Основные проблемы разработки программного обеспечения:
- Ад зависимостей;
- Дублирование;
- Высокое зацепление.
Для борьбы с этими проблемами используются пакетные менеджеры, для эффективной работы таких систем важно правильно версионировать свои проекты. Для этого часто используется "семантическое версионирование".
МАЖОРНАЯ.МИНОРНАЯ.ПАТЧ
- МАЖОРНАЯ увеличивается при нарушении обратной совместимости;
- МИНОРНУЮ при введении новой функциональности, не нарушающей обратную совместимость;
- ПАТЧ при внесении исправлений.
Для автоматических деплоев выполняется регулярное обновление версий, не надо боятся на каждое слияние делать увеличение версии.
Автоверсионирование:
В процессе работы над программным кодом программисты решают следующие задачи:
- внедрение новой функциональности;
- устранение ошибок, допущенных в старых версиях;
- поиск наилучшего решения;
- проверка гипотез;
- создание эксперементального кода.
Для того чтобы организовать работу над кодом необходим инструмент, который позволит решать кажду задачу изолировано и поможет совместить результаты работы нескольких команд разработчиков.
Сегодня основной инструмент для совместной работы над кодом проетка: распределенные системы контроля версий. Наиболее часто встречается git.
Организация коммитов пригодных для машинной обработки и понятной для человека форме. Для этого выделяется структура коммита, которой должны соответствовать все тексты коммитов.
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
- Для автоматизированной обработки важно чтобы типы были стандартизированы, их можно расширять, но базовый набор содержит следующие: feat:, fix:, build:, chore:, ci:, docs:, style:, refactor:, perf:, test:;
- Для коммитов с горячими изменениями предусмотрены 'BREAKING CHANGE: <description>' или восклицательный знак после типа коммита;
- В футере необходимо указывать информацию о связи с багрепортами и задачами.
Существуют разные доработки данной стратегии именования коммитов, одна из популярных - стратегия Angular.
Достоинства:
- автоматизация changelog;
- автоматическое версионирование;
- облегченный поиск по истории коммитов.
Недостатки:
- требует времени для автоматизации навыка;
- проблемы с подбором типов, споры из-за "правильного" подбора типов.
Основной принцип ведения коммитов гласит: делай маленькие коммиты, делай их часто.
Основные проблемы, при ведении коммитов:
- Merge Paranoia – страх перед слиянием, так как слияние как правило вызывает ошибки и конфликты;
- Merge Mania – чрезмерное увлечение слиянием, когда это становится целью, а не средством;
- Big Bang Merge – редкие и большие слияния, которые сложно выполнять;
- Wrong Way Merge – слияние более поздних версий, с более ранними;
- Cascading Branches – создание веток, без фиксации результата в основной ветке;
- Volatile Branches – ветвление состояния, которое является нестабильным;
- Development Freeze – состояние при котором ветка блокирует разработку остальной части команды.
- Berlin Wall – использование веток для разделения обязанностей, вместо разделения
логики и функциональности.
Существует два подхода к развертыванию репозитория в продакшен среду:
- ручной;
- автоматизированный.
При ручном подходе вопрос увеличения версии и момент когда будет выполнен деплой решается индивидуально, поэтому схема ведения репозитория не должна учитывать этот момент. Но для автоматизации CI/CD необходимо предусмотреть не только порядок ветвления и слияния, но и рассмотреть моменты автоинкремента версии и деплоя.
В предложенных далее схемах не предусмотрен автодеплой и версионирование, поэтому я дополнил их исходя из своего опыта организации работы команды по той или иной схеме работы.
Основные подходы к ведению репозитория:
Gitflow это стратегия управления репозиторем, которая обеспечивает хорошую обратную совместимость. Таким образом эта стратегия больше подходит для продуктовой разработки, чем для проектной. Но так как это одна из самых первых стратегий, то она очень распространена и часто встречается на практике.
Достоинства:
- поддерживает релизный подход;
- хорошо показывает себя в продуктах, где нужна обратная совместимость;
- позволяет сопровождать несколько версий продукта;
Недостатки:
- как любой feature-base подход, имеет трудности при слиянии больших веток;
- не подходит для частых релизов;
- история коммитов "засоряется" мерджами, при частых мерджах теряется информативность истории;
- неудобно делать ревью через PR или MR.
Github flow это стратегия управления репозиторием, которая обеспечивает простой способ наращивания функциональности и позволяет обеспечить совместную работу нескольких команд. Эта стратегия чаще используется для проектов, чем продуктов.
Достоинства:
- простой порядок действий;
- одна основная ветка, без релизных веток;
- удобная стратегия, для непрерывной интеграции;
- информативная история коммитов;
- позволяет делать частый деплой;
- поддерживает ревью кода.
Недостатки:
- неудобно использовать при релизном подходе (продуктовая разработка);
- не подходит для обеспечения обратной совместимости;
- простой при "поломке" основной ветки;
- проблемы тестирования при PR.
Данная стратегия учитывает недостатки github flow и позволяет выделять ветки под отдельные релизы, а так же рассматривает наличие отдельной ветки для разработки и развертывание. Эта стратегия нацелена как на продуктовую, так и проектную разработку.
Достоинства:
- Выделение стадии тестирования (last minute tests);
- Более простой порядок действий чем gitflow;
- Гибкое управление необходимыми ветками;
- подходит для разных подходов в разработке;
Недостатки:
- трудности при слиянии больших веток;
- сложно делать частые релизы;
- история коммитов с большим количеством сообщений о мерджах.
Данная стратегия подразумевает наличие одной общей ветки (транка), которая аккумулирует все изменения, в отличии от feature based подходов в транк код вливается регулярно, до фактического окончания работы над фичей.
Особенности:
- быстрые коммиты;
- удобен для CI и частых релизов;
- Feature Flags;
- Branch By Abstraction;
- непрерывный CodeReview (код ревью должен делаться как можно чаще).
В проект добавляется флаг, который указывает на активность фичи. Код мерджится с транком как можно чаще, при этом инкремент версии делается при включении фичи.
Флаги:
- для деплоев;
- для сборок;
- для тестов;
- флаги конфигураций.
Достоинства:
- Мердж может быть сделан в любой момент
- A/B тесты
- Повторное использование кода, из недоработанной фичи
Недостатки:
- Сложный механизм управления
- Вынужденные простои, если ошибочно влит нерабочий код
Ветки используются для изменения одной абстракции. Так как для внедрения новой фичи нужно как правило создать или изменить несколько абстракций, то данный подход подразумевает частые коммиты и должен обеспечивать непротиворечивость состояния транка, т.е. каждое сливание ветки в транк не должно приводить к нерабочему состоянию.
Абстракции выделаются с помощью интерфейсов. Так как изменение абстракции не обязательно влечет изменений всех реализаций, то просто изменить интерфейс нельзя, потому что перестанут работать реализации интерфейса.
Поэтому нормальной практикой является использование наследования для создания промежуточных интерфейсов, которые постепенно заменяются в реализациях.
Общий алгоритм такой:
- создать дополнительную абстракцию к уже существующей (унаследовать);
- слияние;
- перевести часть кода на новую абстракцию;
- слияние;
- перевести следующую часть кода на новую абстракцию;
- когда весь код переведен, отрефакторить код, убрав старый интерфейс.