
Привет, Хабр! Когда релизы выходят раз в две недели, это кажется нормой. Пока не замечаешь, что задачи копятся, а срочные правки превращаются в борьбу с бюрократией. Разработчики привыкают к долгому циклу ожидания, и низкий TTM становится фоном, на который уже никто не обращает внимания.
Мы решили, что так больше продолжаться не может. Вопрос был не просто в скорости — нужно было изменить сам подход. Автоматизировать, упростить, убрать ненужные этапы и сделать так, чтобы код попадал в продакшен быстро, прогнозируемо и без хаоса.
Меня зовут Павел Лиморенко, и сегодня я расскажу вам, как мы к этому пришли, какие проблемы пришлось решать и как нам удалось ускорить релизный процесс API Почты Mail.
Почему мы взялись за ускорение релизов

В какой-то момент стало очевидно, что наши релизные процессы работают медленно. Исторически инфраструктура была развёрнута на bare metal, а каждое развёртывание требовало ручного управления, что неизбежно приводило к задержкам. Код, который разработчики готовы выкатывать, неделями ждал своего часа, пока проходил все этапы согласования.
Релиз включал множество шагов: сборку артефактов, тестирование, тикеты для Ops, подготовку стендов. А если что-то шло не так, то приходилось ждать отката или вручную исправлять ошибки. Итог? Новые фичи попадали в эксплуатацию с огромной задержкой, а срочные исправления превращались в многоступенчатый процесс.
Всё это сказывалось на TTM. Средняя скорость доставки изменений оставалась низкой, разработчики страдали от долгого цикла ожидания, а релизы выходили лишь раз в две недели. Это стало фундаментальной проблемой, которую нужно было решать.
Пора было менять подход: сделать релизы быстрыми, минимизировать бюрократию, автоматизировать процесс и исключить зависимость от ручных этапов.
Как правильно организовать выкатки? Какие есть подходы и что выбрали мы
Вариантов организации релизного процесса предостаточно. Можно идти по схеме Release Train, добавляя изменения, строго по расписанию отрезая накопленные обновления в релиз. Можно работать в формате больших пакетов, накапливая изменения и выпуская их блоками. Но мы пошли другим путём.
Вместо редких и массивных обновлений мы выбрали стратегию частых и небольших итераций. Такой подход позволяет поддерживать мастер-ветку в рабочем состоянии, избегать сложных мерж-конфликтов и контролировать релизы.
Дополнительная фишка — возможность катить изменения сразу после их готовности. Если задача завершена и протестирована, она уходит в эксплуатацию без ожидания ближайшего запланированного релиза. А если вдруг возникает срочная необходимость, например хотфикс, то его моментально можно выпустить. В этой схеме частые итерации снижают вероятность ошибок. Разработчики работают с регулярно обновляемым кодом, а не с массивными пачками изменений, где легко что-то упустить.
Этот подход стал основой всей нашей новой схемы выкатки релизов. Теперь пришло время внедрять его на практике.
Как мы готовились к новой схеме

Чтобы перейти на частые и прогнозируемые релизы, нам понадобилось подготовить инфраструктуру, пересмотреть подходы к тестированию и учесть множество технических нюансов.
Первым шагом стало объединение всех проектов API, тестов и документации в монорепозиторий. Это позволило хранить весь код в одном месте, упростить работу с изменениями и минимизировать сложные зависимости между сервисами. Однако при этом возникла новая задача: как избежать ненужных выкатываний всего репозитория при изменениях?

Мы внедрили систему тегирования, которая фиксирует состояние кода и перед запуском конвеера проверяет, действительно ли произошли изменения. При каждом обновлении используется механизм вычисления SHA от кода (вместо commit SHA), который сверяет актуальность данных и предотвращает избыточные развёртывания. Благодаря этому конвейер разворачивает только те компоненты, которые действительно требуют выкладки, а не весь код целиком, что сокращает время релиза и повышает его предсказуемость.
Следующим шагом стал перенос сервисов в Kubernetes. Это позволило уйти от работы с bare metal и добиться гибкости в CI/CD. До этого каждое развёртывание требовало ручных действий, занимало дополнительное время и создавало риски. Теперь окружения развёртываются автоматически, а стабильность процессов значительно улучшилась.
Мы также изменили подход к тестированию. Теперь тестирование проводится полными регрессами per task: каждая новая задача полностью проверяется перед выкаткой, без дополнительных финальных этапов. Это даёт уверенность, что изменения каждой задачи стабильны и готовы к релизу.
Кроме того, мы отказались от финального тестирования готового релиза. Релиз-инженер больше не нужен для разруливания релиза, код проверяется на каждом шаге, а выкатка происходит автоматически. С учётом небольшого размера итераций считаем, что вероятность конфликта мала, плюс этот риск митигируется плавной раскаткой с помощью Argo Rollouts.
Таким образом мы подготовили систему к новому подходу: получили частые релизы, стабильный код и минимум бюрократии.
Как устроен конвейер

Автоматизация релизов невозможна без чётко настроенного конвейера. Мы переработали его структуру, внедрив механизмы быстрой и предсказуемой выкатки.
Разработчик пишет код, запускает тесты и создаёт merge request. После мержа этого MR в мастер сразу собирается Docker-контейнер, заливается в registry. Далее создаётся merge request в репозиторий IaC, где прописываются теги собранных образов, а развёртывание на staging-стенд запускается триггером. После успешного прохождения всех проверок MR в репозитории IaC вливается в мастер, и далее там запускается конвейер, который выполняет финальное развёртывание.
Этапы работы конвейера:
Он вычисляет SHA от каждого сервиса в монорепозитории.
Затем сверяет их с SHA в тегах из репозитория с манифестами, чтобы решить, надо ли что-то из них выкатывать.
Уведомляет в мессенджере о новом вливании ветки.
Собирает контейнеры, помещает их в реестр, создаёт merge request в репозиторий с манифестами.
Ждёт выполнения там каких-то процедур, например, выкатки на stage.
Затем ждёт, пока кто-то ответственный запустит вливание MR, созданного в четвёртом пункте.
После этого уведомляет в мессенджере о начале выкатывания, о выполняемых задачах и ответственных за них разработчиках.

Мы сделали выкатку триггерной, чтобы исключить человеческий фактор и минимизировать время между вливанием и эксплуатацией. Код автоматически проверяется, а запуск конвейера происходит с учётом проверенного механизма сборки. Такой подход устраняет риск задержек и ошибок, связанных с ручными шагами. Таким образом, конвейер стал быстрым, надёжным и понятным.
Как мы следим за конвейером
Когда релизный процесс полностью автоматизирован, важно иметь чёткую систему мониторинга. Чтобы мгновенно реагировать на сбои, отслеживать статус релиза и обеспечивать прозрачность для всей команды, мы настроили контроль на каждом этапе:
Автоматические уведомления сигнализируют о каждом значимом изменении. Разработчик получает уведомления при вливаниях, запуске конвейеров и развёртывании, независимо от его успешности.
Графики и метрики помогают анализировать статус конвейеров в реальном времени, отслеживать время выполнения, долю успешных релизов и выявлять узкие места, требующие оптимизации.
Если что-то идёт не так, детальное изучение журналов позволяет оперативно найти причину ошибки и устранить проблему.
Таким образом, релизный процесс стал полностью прозрачным, а его контроль — удобным и предсказуемым.
Особенности организации конвейеров в CI
CI — мощный инструмент, но его использование в запуске в эксплуатацию требует особого внимания к деталям. В ходе автоматизации мы столкнулись с несколькими проблемами и нашли эффективные способы их решения:
Сложные задачи с несколькими зависимостями. Когда на одном этапе выполняется несколько действий, есть риск сбоя на каком-то из них и невозможности дальше использовать пайплайн. Мы вынесли ключевые операции в отдельные блоки, чтобы минимизировать точки отказа и дать возможность перезапускать эти отдельные блоки.
GitLab API. Вызов API из скриптов в рамках пайплайна даёт широкие возможности по автоматизации, которые недоступны никак по-другому.
Auto-cancel redundant pipelines. Полезная фича, которая автоматически отменяет предыдущие запущенные процессы. Но в случае релизного конвейера это может привести к нежелательным отменам релизных пайплайнов. Мы сделали задачи непрерываемыми (interruptable: false) и настроили через API отмену предыдущих процессов в рамках этих же пайплайнов.
Идемпотентные задачи. Чтобы избежать лишних повторов выполнения одних и тех же операций, мы внедрили механизм проверки состояния перед запуском. Если данные уже актуальны, то задача просто пропускается.
Таким образом, CI стал не просто инструментом автоматизации, а надёжной системой, которая предсказуемо управляет релизами.
Что получили в итоге
20-кратное ускорение релизов! Вместо одного выпуска раз в две недели теперь мы выкатываем обновления два раза в день.
Упрощение процессов. В core-команде больше нет инженера релизов. Ответственность за задачу теперь лежит на разработчике, а релиз происходит автоматически.
Более гранулярные релизы. Вместо массивных пакетов обновлений мы выпускаем 5–6 задач за раз, что делает кодовую базу предсказуемой и управляемой.
Автоматизированный расчёт DORA-метрик. Теперь мы точно знаем, сколько времени занимает каждая правка, и можем оптимизировать процессы на основе реальных данных.
Наш подход к релизам изменился. Теперь это быстрый, гибкий и прозрачный процесс, который делает жизнь разработчиков проще, а продукт — лучше. Надеюсь для кого-то наш опыт будет полезен, а своим опытом автоматизации делитесь в комментариях!
gudvinr
"Почта Mail" - это все равно что "Почта Почта", звучит крайне странно.
Почему не просто API Почты или Mail API?