Привет, Хабр! Я Федор Щудло, team lead и fullstack-разработчик. Всего я в разработке 16 лет, из них 12 в роли team lead.
Три года назад я сменил работу и занялся проектом, состояние которого можно описать кратко: ему 25 лет.
За этот долгий срок проект пережил несколько слияний и разделений компании, означающих серьезные потери людей, знаний, и даже исходников от некоторых сервисов по юридическим соображениям.
На проекте были благополучные периоды, когда были созданы очень крутые и амбициозные вещи. Но были также периоды, когда команды еле хватало на выполнение самых срочных задач. И в это время многие сделанные или не доделанные большие штуки изрядно обветшали.
Как результат, разработка шла с большими накладными расходами (все делали долго), и с высокими рисками (выкатили и разломали прод). А команда при этом работала на износ.
Но за три прошедших года мы с командой кардинально изменили ситуацию. В этой статье я расскажу про самую значимую перемену — простую, но кратно снизившую и накладные расходы, и риски. А это уже открыло дорогу сотням маленьких изменений, в итоге преобразивших проект.
В чем соль проблемы
Когда мы сняли все «низко висящие фрукты» , перед нами встал вопрос: «А что же делать дальше?». Проблема была не в том, что мы не знали, что делать. Наоборот, сделать можно было очень много всего. Но каждое изменение требовало серьезных вложений, поэтому было трудно выбрать самое важное.
И в принятии этого решения нам очень помогли DORA-метрики.
Краткий экскурс в DORA-метрики
DevOps Research and Assessment (DORA) это многолетнее исследование подходов к разработке ПО в тысячах компаний из самых разных отраслей. Его задачей было найти подтвержденные корреляции между принятыми в компании практиками разработки ПО и успехом компании на рынке.
Наиболее полно исследование описано в книге Accelerate: The Science of Lean Software and DevOps: Building and Scaling High Performing Technology Organizations, которую я рекомендую прочитать, если вам интересна тема эффективности разработки ПО. Здесь же я лишь кратко перескажу суть.
Первым важным результатом исследования стал список из трех десятков инженерных, процессных и культурных практик, применение которых статистически значимо коррелирует с успехом компании на рынке.
![Список практик, значимо влияющих на успех компании согласно DORA research Список практик, значимо влияющих на успех компании согласно DORA research](https://habrastorage.org/getpro/habr/upload_files/257/2bf/fd9/2572bffd96a977f74a3093b4b18ef19b.png)
Вторым важным результатом стали четыре простые метрики, позволяющие оценить процесс разработки ПО. Они разделены на две категории:
Throughput-метрики:
Deployment frequency: как часто команда релизит свою систему.
Commit delivery lead time: время, за которое сделанный коммит “доезжает” до прода.
Stability-метрики:
Deployment failure rate: процент релизов, закончившихся поломкой.
Mean time to recovery: среднее время на восстановление после поломки.
Участвовавшие в исследовании компании были поделены на 4 категории: Low/Medium/High performers и Elite. По метрикам составили “бенчмарк”, позволяющий оценить вашу компанию относительно индустрии в целом.
![Таблица позаимствована из State of DevOps Report 2019 Таблица позаимствована из State of DevOps Report 2019](https://habrastorage.org/getpro/habr/upload_files/4ae/1cf/05b/4ae1cf05b819c47fa1c8521439a883fe.png)
Согласно исследованию, эти четыре метрики «реагируют» на все нижележащие недостатки, будь то плохо спроектированный код, множество ручной работы или наличие бюрократических барьеров в процессе разработки ПО. Таким образом, они определяют эффективность процесса разработки в целом.
Примечание: после приобретения DORA компанией Google, была добавлена пятая метрика - Reliability. Но в данной статье я буду придерживаться модели из книги Accelerate, поскольку там хорошо описана связанность и значимость именно четырех метрик.
Вообще попытки измерить эффективность разработки ПО вещь не новая. Но прежние метрики — количество строчек кода, процент покрытия тестами и скорость закрытия задач — скорее стали мемами, чем принесли пользу. Опыт компаний показывает, что привязка зарплаты сотрудника к количеству строчек кода приводит лишь к максимально раздутому коду. А превращение test coverage в OKR к появлению множества тестов без единого assert-а.
Чем DORA-метрики лучше?
DORA-метрики сбалансированы. Если повышать throughput нездоровыми способами (например, заставлять команду часто релизить несмотря на плохо реализованный конвейер поставки), то просядут метрики stability. И наоборот.
DORA-метрики исключают индивидуальную оценку сотрудников, что способствует созданию здоровой blameless-культуры. Также эти метрики значимо коррелируют с выгоранием сотрудников.
DORA-метрики измеряют реальную работу, поэтому их гораздо сложнее «хакнуть». Например, скорость закрытия задач можно «улучшить» просто начав дробить задачи на более мелкие и заводить по задаче на каждый чих. Как вы понимаете, реальное положение дел на проекте такое «улучшение» не изменит.
Возвращаемся к нашему проекту
Итак, мы реализовали сбор DORA-метрик из нашей CI системы (Jenkins) и визуализировали их в Grafana. Вот что мы увидели спустя месяц:
![DORA-метрики для нашего проекта за первый месяц их сбора DORA-метрики для нашего проекта за первый месяц их сбора](https://habrastorage.org/getpro/habr/upload_files/447/256/fad/447256faddf9180beda2a0eb82f9ab85.png)
Согласно бенчмарку DORA мы перформили лучше, чем 57% компаний в технологической индустрии. Релизили мы раз в два дня - уровень high performers (зеленый цвет). Commit delivery lead time и mean time to recovery были на уровне medium performers (желтый цвет).
А вот с частотой падений при релизе у нас все было печально. И, поскольку все 4 метрики тесно связаны между собой, низкая стабильность релизов перечеркивала все остальное.
Чтобы понять значение этих цифр на практике, представьте себя разработчиком на подобном проекте. Допустим, при выполнении задачи вы увидели возможность сделать небольшое улучшение: удалить (вроде бы) мертвый код или обновить зависимость. При этом вы понимаете, что:
Ваше изменение поедет на прод через полторы недели, когда вы уже будете погружены в другую задачу.
С вероятностью 25% релиз развалится. Совсем не факт, что из-за ваших изменений, но это еще придется выяснить.
На починку прода уйдет 29 часов. То есть, буквально, день и ночь.
Станете вы делать маленькое улучшение в таких условиях или предпочтете не рисковать? Скорее всего, второе. Для этого даже не нужно знать точные цифры, прошлый опыт работы на проекте и так подскажет, что лучше лишний раз не рисковать.
Причем со временем ситуация будет становиться только хуже, сама собой. Если мы боимся изменять код, то он будет все больше обрастать недочетами, его будет все сложнее понимать, и будущие изменения от этого станут еще сложнее. В системном мышлении такое состояние известно как «порочный круг» (vicious cycle). Если это продолжается годами, то результат печальный — полная потеря контроля над кодом (lost ownership).
Однако, порочный круг можно превратить в «круг процветания» (virtuous cycle): чем легче нам делать небольшие изменения, тем чаще мы их делаем и тем проще их будет делать в дальнейшем.
Теперь стало очевидно, куда нам вложиться в первую очередь — в стабильность и простоту конвейера поставки.
Как выглядел наш конвейер поставки
Наш проект состоит из 14 сервисов, которые разрабатываются и поставляются более или менее идентично. На картинке ниже отображены ветки в git и конвейеры в CI, через которые проходил код от разработки до релиза:
![Конвейер поставки до внесенных изменений Конвейер поставки до внесенных изменений](https://habrastorage.org/getpro/habr/upload_files/608/ccd/a4c/608ccda4ccdf2b09ad420b3f8d9f017f.png)
Главный изъян такого pipeline — это многократное повторение идентичных шагов: сборка, юнит-тесты и e2e-тесты. И самым узким местом были именно e2e-тесты.
Вообще наши e2e-тесты великолепны — это создание четырех кластеров-реплик production, на которых в параллельном режиме запускается 3500 тестов, покрывающих все значимые сценарии.
Однако, у них были (и отчасти остались) существенные минусы:
Время выполнения варьировалось от 30 до 90 минут.
В один момент времени можно делать только один прогон тестов.
e2e-тесты флакуют. Метрики показали, что в худшие периоды падал каждый второй запуск, а в среднем каждый четвертый.
Тесты было трудно дебажить и чинить. Метрики показали, что в среднем на починку уходило 5 дней.
Теперь давайте представим, что нам нужно устранить критическую уязвимость в log4j, который используется во всех 14 сервисах. Это показательный пример, поскольку само по себе обновление зависимости занимает считанные минуты. Но накладные расходы на его доставку до прода у нас были просто огромны.
С описанным выше конвейером нам потребуется: создать 14 pull requests, пройти 14 сборок с e2e-тестами (с учетом падений нам понадобится ~28 запусков). Каждый запуск это 30-90 минут. Итого ~4 рабочих дня. Далее, те же действия для веток develop и master - еще ~8 дней. И, наконец, 14 релизов — ещё полдня. Итого, ~12 рабочих дней непрерывной, совершенно не интеллектуальной работы. Конечно, можно срезать углы и сразу запушить в master. Но это все равно 28 запусков e2e-тестов и ~4 дня прежде чем критический фикс попадет на прод.
Как лечить подобные проблемы прекрасно описано в книгах (например, в канонической Continuous Delivery: Reliable Software Releases through Build, Test, and Deployment Automation).
Вот что мы сделали:
Максимально упростили и автоматизировали deployment pipeline, добавили возможность выкатывать хоть 1, хоть все 14 сервисов за раз. Собственно, релизит теперь Slack bot, а задача человека просто поставить несколько галочек, чтобы выкатить нужные сервисы и разослать уведомления с release notes.
Покрыли health check-ами и alert-ами все критические сценарии, чтобы обнаруживать проблемы сразу при релизе. Это помогло быстрее локализовывать и устранять проблемы.
Переписали пайплайн e2e-тестов, чтобы он создавал релизные артефакты для всех 14 сервисов сразу (все равно они тестировались вместе). Теперь в develop и в master вместо 14-28 прогонов e2e-тестов на каждую ветку нужно 1-2 прогона. Это радикально снизило нагрузку на наш главный bottleneck - e2e-тесты.
Избавились от ветки develop и начали сливать сразу в master. Так мы сэкономили на 14 слияниях и еще снизили нагрузку на e2e-тесты.
Раньше в feature branches e2e-тесты запускались по умолчанию. Мы отключили это, но оставили возможность запустить e2e-тесты при необходимости. С командой мы договорились, что, если в master попадают изменения, ломающие e2e-тесты, то мы не только чиним e2e, но и закрываем этот сценарий unit-тестами в конкретном сервисе. Далее мы 3 месяца следили по метрикам, не упала ли стабильность от такого решения. Она не упала. Так мы еще снизили нагрузку на e2e-тесты.
Вот как выглядит наш конвейер поставки теперь:
![](https://habrastorage.org/getpro/habr/upload_files/aef/ab3/ccc/aefab3cccfc73fc7b6a375385d474b82.png)
Теперь для устранения той же уязвимости в log4j нужно сделать 14 pull requests, получить 14 зеленых сборок (без e2e-тестов это в среднем 4 минуты). Далее сливаем в master и один раз получаем зеленые e2e-тесты (с поправкой на частоту падения это 2 запуска — стабильностью тестов мы еще не занялись). И, наконец, 1 релиз для всех 14 сервисов.
Итого 3-4 часа вместо исходных 2-3 недель. Позже мы прикрутили renovate и подобные обновления стали занимать вообще считанные минуты времени разработчика, но это уже отдельная история.
Описанные изменения мы запускали последовательно в течение января 2023 года. И, как вы можете видеть по графикам ниже, начиная с февраля 2023 ситуация изменилась кардинально:
![Deployment frequency. Декабрь 2022-Декабрь 2023 Deployment frequency. Декабрь 2022-Декабрь 2023](https://habrastorage.org/getpro/habr/upload_files/f5a/fd2/c1d/f5afd2c1df453b36044a2d02bef305ce.png)
![Commit delivery lead time. Декабрь 2022-Декабрь 2023 Commit delivery lead time. Декабрь 2022-Декабрь 2023](https://habrastorage.org/getpro/habr/upload_files/01c/2ee/cc5/01c2eecc58b57042496ebe129d807446.png)
![Deployments failure rate. Декабрь 2022-Декабрь 2023 Deployments failure rate. Декабрь 2022-Декабрь 2023](https://habrastorage.org/getpro/habr/upload_files/099/193/97f/09919397f3b13ee033195ae698119bba.png)
![Mean time to recovery. Декабрь 2022-Декабрь 2023 Mean time to recovery. Декабрь 2022-Декабрь 2023](https://habrastorage.org/getpro/habr/upload_files/d64/f65/1c4/d64f651c468d5e0c2d1a55d371d7a816.png)
И вот теперь, когда внесение изменений стало проще и безопаснее, мы вплотную занялись скоростью, стабильностью и простотой отладки e2e-тестов. Также, с февраля по май мы ловили каждую возникающую в релизе проблему и сделали под сотню маленьких улучшений, которые позволили навсегда забыть об описанных выше проблемах.
Вот как выглядят наши метрики теперь, по сравнению с исходными:
![DORA-метрики за первый месяц их сбора DORA-метрики за первый месяц их сбора](https://habrastorage.org/getpro/habr/upload_files/411/1ee/520/4111ee52094e8dfe235b817b2ea2dcc8.png)
![DORA-метрики за последние полгода DORA-метрики за последние полгода](https://habrastorage.org/getpro/habr/upload_files/a97/5d5/73b/a975d573b830980525db1c7cab8fa996.png)
Согласно бенчмарку DORA, теперь мы перформим лучше чем 94% компаний.
Почему это метрики изменившие проект полностью?
К уже показанным цифрам, я могу добавить личные ощущения. Настроение в команде за последний год кардинально поменялось. Стремящиеся к нулю накладные расходы открыли дорогу куче маленьких улучшений, которые раньше делать было просто страшно. Теперь развитие проекта перестало быть постоянным сверхусилием и оно идет совершенно с иной скоростью.
Разумеется, у нас все еще осталось немало проблем. Но теперь есть уверенность, что мы до всего доберемся, это лишь вопрос времени.
Раньше мы собирались на релиз всей командой и выполняли его в строго отведенные временные окна, чтобы не испортить жизнь пользователям. И храни тебя вселенная от релиза вечером, в пятницу или перед праздниками. Сейчас любой участник команды запускает релиз тогда, когда ему нужно.
И метрики поставки это не единственное, что стало лучше. Например, время от первого коммита в feature branch до слияния в master за три года сократилось с 9 до 1.5 дней. Думаю, это многое говорит об изменениях в сопровождаемости кода и качестве инструментов. Если же вы верите в скорость закрытия задач как в ценную метрику, то теперь мы выполняем задачи в среднем за 4 дня. Раньше это было 11 дней.
А еще теперь мы можем вещи, которые раньше было трудно даже представить. Например, всего за 4 месяца мы истребили копившийся годами долг по security и выкатили 400 релизов (внимательный читатель мог заметить по графику deployment frequency, что в ноябре и декабре у нас было по 100+ релизов в месяц). И даже при такой интенсивности релизов мы не зацепили ни одного пользователя.
Вместо заключения
Надеюсь, вам понравилась эта история и она дала вам уверенность, что любой проект можно вытащить из ямы технического долга.
Если вы захотите повторить описанный опыт, то вот репозиторий с таблицами (Postgres) и Grafana-дашбордом для DORA-метрик, которые используем мы. Правда реализовать заполнение таблиц данными из CI-системы вам придется самим. Эта часть не обобщается. Или, вы можете взять инструмент Four Keys, продвигаемый командой DORA, если он вам подходит больше.
Но прошу вас: прочтите книгу Accelerate, если хотите получить пользу от этих метрик. В ней описано гораздо больше, чем я смог упомянуть в одной статье. Например, книга предостерегает от “токсичного” использования DORA-метрик: превращения их в ОКР, сравнения команд и тому подобных действий. Также, я очень рекомендую вам посмотреть доклад Брайана Финстера How to Misuse DORA DevOps Metrics.