Код-ревью без очередей
Программисты пишут код (удивил, да?) Если это пет-проект, то вы вольны делать со своим кодом все, что хотите. Но когда над одним проектом работает несколько человек или даже целая команда, рано или поздно встаёт вопрос о необходимости код-ревью. Кому отдать на ревью? Как ускорить этот процесс? Как равномерно распределять реквесты по ревьюерам? Вопросов много, а ответы не так очевидны. В этой статье расскажу, с какой проблемой мы столкнулись в команде автотестирования в Wrike, как у нас устроен процесс ревью и зачем нам понадобился самописный сервис.
С какой проблемой мы столкнулись в процессе разработки автотестов
В Wrike процесс доставки новой версии продукта от разработчиков до клиентов развит настолько хорошо, что деплои у нас происходят минимум раз в день, а иногда и чаще. В этом, помимо прочего, немалая заслуга автоматического тестирования. У нас есть тесты всех сортов и расцветок: unit-тесты, всевозможные REST-тесты, UI-тесты на базе Selenium, скриншотные, мобильные, нагрузочные и даже тесты на тестовые утилиты.
В середине 2016-го года мы переписали фреймворк UI-тестов, реализовав паттерн PageObjects-Steps-Test. Писать тесты стало настолько просто, что количество их авторов перевалило за сотню (активных — больше пятидесяти). Писали все: от QA-инженеров до backend и frontend разработчиков. Но когда в проект коммитят все кому не лень много людей, даже несмотря на расписанный код-стайл, невозможно поддерживать чистоту кода без ревью, которым занимается команда автоматизаторов (а нас на тот момент было всего 10 человек). А еще мердж-реквесты (дальше в тексте — МР) надо каким-то образом равномерно распределять среди ревьюеров.
Вы, наверное, догадываетесь, какая из всего этого вытекает проблема — ревью становится долгим. А ведь после изначального ревью могут потребоваться правки, порой и не один раз, потом снова ревью и т.д. Каждая итерация ревью-правок занимает много времени: никто не хочет бросать свои дела и переключаться, а GitLab шлет уведомления об изменениях в МР-ах на email (почту нынче мало кто постоянно проверяет), да еще и с запозданием. Все это привело к тому, что ревью у нас в среднем занимало больше 100 часов в 80 перцентиле (даже если исключить выходные дни).
Новые тесты или фиксы старых зачастую связаны с изменениями в продукте, затянувшееся ревью тормозит релиз frontend или backend ветки, что, в свою очередь, ломает планы команд по релизам. Поэтому мы хотели снизить время ревью до 48 часов максимум (80 перцентиль).
Варианты решения проблемы
Чтобы снизить время ревью, мы начали с самого простого: решили явно раздавать мердж-реквесты ревьюерам. Два лида из команды по очереди каждый день просматривали список новых реквестов в GitLab и назначали на них инженеров из команды, пытаясь сохранять примерно одинаковую нагрузку на каждого реьвюера. Это помогло не сильно, потому что основная проблема с уведомлениями на каждом этапе ревью осталась нерешенной. К тому же это занимало время лидов, отрывая их от более важных задач.
Тогда я решил разработать сервис, который сможет контролировать все изменения и присылать уведомления ответственным в личные сообщения в Slack.
Изначально от сервиса требовалось только оперативно рассылать уведомления об изменениях в МР-е и его переходе между этапами ревью, правок после ревью и обратно. Чтобы понимать, что изменилось в MP-e, решил хранить прошлое состояние в базе данных. Это позволяло еще и получать статистику по времени жизни реквестов, по всем авторам и ревьюерам.
Первая итерация: сервис «на коленке» и автоматическое назначение ревьюеров
Любой утилите нужно имя, хотя бы чтобы не называть её «ну тот бот, который с ревью помогает». Тем более, когда это проект, который ты сам сделал с нуля. Хотелось, чтобы название перекликалось с системой Git, с которой и работает бот. Вариантов было несколько, но в конечном итоге остановился на названии JiGit — коротко и про Git. Дальше так и буду его называть.
На первых порах JiGit создавался «на коленке»: по ночам в виде факультатива. Поэтому я выбрал знакомый и простой способ реализации — в виде теста. Unit-тесты в проекте уже были, как запускать в CI знаем. Почему бы и нет?
Что делал «тест» с технической стороны? Сначала запрашивались все открытые мердж-реквесты из GitLab для нашего проекта. Далее для каждого реквеста текущее состоящие сравнивалось с тем, что сохранено в базе. Автор и/или ревьюер получал уведомление о конкретном изменении. В конце текущее состояние перезаписывало сохраненное в базе. Один запуск по всем МР-ам —, а их было в среднем в районе 20 — занимал от 1 до 1,5 минут. «Тест» запускался каждые 5 минут — время задержки оповещений было от 0 до 5 минут.
Первым шагом для дальнейшего развития сервиса стало автоматическое назначение ревьюеров.
Для этого необходимо было учесть:
Нагрузку на ревьюеров, чтобы всем доставалось поровну.
Отпуска и болезни ревьюеров (иначе МР может ждать пару недель, а это не вписывалось в наши требования).
Сложность кода, ведь можно как поменять название переменной, так и порефакторить весь проект. Не у всех для этого есть достаточно экспертизы.
Нагрузку я брал из базы за последние 60 дней. Тогда даже если ревьюер уходит в отпуск на пару недель, то по возвращении на него не сваливаются сразу 10 МР-ов. Даты отпусков мы отмечаем в графике дежурств: каждый день нужен ответственный QAA-инженер для релиза новой версии продукта. Из этого графика JiGit узнаёт, на кого назначать МР-ы не стоит.
А пункт со сложностью кода мы решили введением ряда лейблов в GitLab. Автор мердж-реквеста «клеит» на него лейбл с соответствующей сложностью, а JiGit знает, мердж-реквесты какой сложности может смотреть каждый ревьюер. Мы выделили пять уровней сложности и описали, в каком случае ставить каждый уровень. Если кто-то из авторов забудет поставить лейбл, то JiGit об этом любезно напомнит.
Когда код МР-а вмёрдживался в мастер, JiGit высчитывал время, которое заняло ревью и сохранял его в базу. Так мы получили метрику контроля времени ревью.
Вторая итерация: SpringBoot и веб-интерфейс
Шло время, JiGit нужно было подключать к другим QAA-проектам, которые тоже разрослись до неприличных размеров. Еще и разработчики из других команд узнали про инструмент и захотели себе такой же. Архитектура в виде JUnit-теста не была достаточно гибкой, а связность была настолько высокой, что любое изменение кода превращалось в лотерею — неизвестно, где может «стрельнуть». И тогда настало время для глобального рефакторинга.
Я решил перенести JiGit на рельсы SpringBoot.
Причин было несколько:
У нас в команде к тому моменту уже появился достаточный опыт в использовании SpringBoot.
Если тебе нужен сервис, работающий 24/7, это первый фреймворк, который приходит на ум.
С ростом числа проектов увеличивается время между каждым последовательным запуском, а значит и время реакции JiGit на изменения в мердж-реквестах. Отсюда появилось решение перейти на работу с вебхуками вместо самостоятельных обращений к GitLab. Заодно и нагрузку на GitLab снизим.
Раз уж взялись за рефакторинг, нужно было заодно решить накопившиеся проблемы: большая связность кода, невозможность гибкой настройки под разные проекты, повсеместный hardcode и другие.
К примеру, в коде огромное количество условных операторов if. Если в проекте, который нужно подключить к JiGit, другой процесс ревью, придется добавить еще несколько операторов. Становится страшновато, особенно за моих последователей, которым придется во всём этом разбираться.
Вот небольшой пример кода:
if (!equalLabels(gitlabMr, dbMr)) {
List addedLabels = getAddedLabels(gitlabMr, dbMr);
List removedLabels = getRemovedLabels(gitlabMr, dbMr);
if (hasReviewer(gitlabMr)
&& !addedLabels.contains(REVIEW_OK)
&& (removedLabels.contains(TO_FIX)
|| addedLabels.contains(WAITING_FOR_REVIEW))) {
infoMsg(gitlabMr.getReviewer(), NEED_REVIEW_ASSIGNEE
.format(gitlabMr.getWebUrl()));
} else {
infoMsg(gitlabMr.getAuthor(), LABELS_CHANGED
.format(gitlabMr.getLabels(), gitlabMr.getWebUrl()));
}
}
По мере разработки всё более остро чувствовалась необходимость в веб-интерфейсе для JiGit. И помогло, как всегда, удачное стечение обстоятельств. Команда вебсайта узнала о наших наработках и захотела подключить его в свой проект, где на тот момент были те же проблемы с ревью. Я подключил им JiGit, а заодно мы обсудили пожелания по UI с обеих сторон (ведь UI нужна и бэкендная часть), расписали ТЗ, и ребята разработали и запустили интерфейс для JiGit (за что им огромное спасибо!).
С помощью интерфейса можно указывать список ревьюеров в каждом проекте, а также даты их отпуска или больничного. Там же можно смотреть метрики по ревью, строить графики, подключать новые проекты в JiGit и настраивать их.
Какие проблемы помог решить сервис
От внедрения JiGit мы получили много пользы.
Время код-ревью сократилось. Как и ожидалось, метрики по срокам ревью стали заметно снижаться. Теперь среднее время ревью стало колебаться в районе 24 часов, что видно на графике. И это при том, что количество МР-ов выросло со 150 до 350 в месяц!
Merge Requests Lifetime
Метрики помогают контролировать процесс ревью. Имея данные по длительности ревью мердж-реквестов, мы можем своевременно исследовать причины увеличения метрик и реагировать на них.
Процесс ревью стал более прозрачным. JiGit помогает понять дальнейшие шаги ревью на каждом этапе, двигаться по процессу и исправлять ошибки, которые могут потенциально затягивать сроки ревью. Например, пишет об упавших пайплайнах или напоминает о необходимости добавить или снять нужные лейблы.
Мердж-реквесты распределяются автоматически. Теперь даже самый далекий от автоматизации человек в компании может написать свои тесты, создать мёрдж-реквест и не думать о том, кто должен проводить ревью его кода. JiGit самостоятельно назначает ревьюера.
Процесс разработки тестов можно контролировать на всех этапах. С помощью сервиса появилась возможность контролировать разработку тестов на всех этапах от создания МР-а до мёрджа ветки в мастер. Раньше приходилось полагаться только на ответственность авторов кода и ревьюеров, а также на pre-commit хуки GitLab. Это оставляло возможность случайно или специально обойти принятый процесс ревью и смержить любой код в мастер.
Теперь JiGit не позволит этого сделать. Чтобы код из ветки попал в мастер, необходимо, чтобы все пайплайны по ветке были зеленые, был создан мёрж-реквест, на него был назначен ревьюер из списка согласованных, именно он поставил «ревью ОК», после ревью не прилетали дополнительные коммиты и т.д. и т.п. Теперь мы уверены, что в мастер не просочится «кривой» код, только если ревьюер по ошибке не пропустит эти изменения. Но полностью человеческий фактор исключить невозможно. Хотя… Может подключить ML и обучить JiGit автоматическому ревью?
Быстрый и понятный процесс помогает вовлекать коллег в написание тестов. Наряду с обширной документацией по использованию проекта, процесс ревью, который стал прозрачным и быстрым, уже не отталкивает желающих написать тесты. В нашей компании автотесты пишут не только в QAA-команде, но и QA-инженеры, фронтенд и бэкенд разработчики, за что им большое спасибо.
Инженеры из других отделов начали использовать JiGit. Он быстро получил известность в инженерном департаменте компании. Многие команды стали обращаться с просьбой подключить JiGit к их проектам, где были схожие проблемы с длительностью ревью, прозрачностью процессов и сбором метрик. Подход к процессу ревью, налаженный в команде QAA, пришелся по вкусу, и некоторые команды даже поменяли свои процессы ревью, чтобы проще подключать JiGit к их проекту и получать от него большую функциональность.
Планы по развитию сервиса
У сервиса JiGit большое будущее, как минимум в Wrike. Сейчас он уже подключен к шести проектам, а в планах подключить и ко всем остальным.
Мы работаем над улучшениями функциональности как в общем, так и для отдельных проектов.
Из основных задач на будущее:
Автоматическое определение сложности мёрдж-реквеста.
Возможность задавать ревьюеров в зависимости от части проекта, в которой были изменения.
Поддержка Wrike API.
Начальная настройка сервиса через Web-UI для разворачивания с нуля.
В светлом будущем мы хотим выложить проект в opensource, ведь наверняка у многих из вас есть или будут похожие проблемы с ревью. Да и сила коллективного сознания может помочь с развитием.
А как выглядит процесс ревью в вашей компании? Буду рад, если поделитесь опытом и идеями по поводу сервиса в комментариях. Спасибо за внимание!