Для множества команд рефакторинг — это боль. Потому что если ты занимаешься рефакторингом, то не разрабатываешь основной продукт, а если не занимаешься — растет технический долг проекта. В какой-то момент команды приходят к мысли: «Давайте разграничим рефакторинг и разработку и выделим на него, например, 20% наших человеко-часов, а остальное время продолжим заниматься разработкой!» Мысль эта неплохая, вот только дело в том, что на каждые 100 разработка-часов вы никогда не получите 20 чистых часа рефакторинга. Потому что «разработка» — это не только работа с кодом.
Если мы говорим о зрелых командах, а не сжатых в материальную точку мини-коллективах на 3-5 человек, то «разработка» включает в себя еще целую массу различных активностей команды кроме написания кода. В «разработку» можно записать так нелюбимые многими митинги, работу с документацией, ведение отчетности в таск-менеджерах и так далее. Все это съедает примерно 30% от наших часов, выделенных на разработку. И как-то незаметно у нас получается, что вместо картины «80 часов кодим, 20 часов рефакторим» мы получаем неприглядную цифру в ~13 часов на, непосредственно, сам рефакторинг, потому что все остальное было поглощено другими активностями.
Ниже мы расскажем, как все же совместить разработку с рефакторингом так, чтобы технический долг проекта не рос, будто снежный ком, а еще поговорим о правильном распределении времени и расстановке приоритетов.
Почему вообще рефакторинг вызывает столько проблем? В первую очередь по причине того, что команды не могут адекватно оценить собственные ресурсы и совместить этот процесс с разработкой. Людям кажется, что если их рабочий день составляет 8 часов в день (40 в неделю), то все 40 часов они, якобы, занимаются разработкой. Это не так.
Весь процесс разработки можно разделить на две части: это побочные активности и все, что связано непосредственно с кодом.
Вот как выглядит распределение времени одной из наших команд разработки
У читателя может возникнуть резонный вопрос: «а как вы построили эту диаграмму?» и сейчас мы постараемся дать ответ. Данные были взяты не с потолка, а на основе нашей внутренней статистики. Наши разработчики ведут учет своей активности в Jira. Каждый такой персональный ворклог состоит из четырех разделов: код-ревью, технические обсуждения, конкретные тикеты и «все остальное». Благодаря этой достаточно простой и прозрачной классификации мы и построили аналитику того, сколько времени в каких командах мы тратим на каждый аспект разработки. Если на встречи, стендапы и ревью выпадает треть всего рабочего времени — все в пределах нормы. Если времени на эти активности тратится больше 33% — то в команде есть проблемы и их надо решать.
Вроде бы, тут нет подвоха и все логично, но как нам вписать в эту историю рефакторинг? Объявить «месяц рефакторинга» и забить на разработку? Однако у любого коммерческого продукта есть свой график, выбиваться из которого крайне нежелательно. Еще рефакторинг похож на трясину: если начал им заниматься, то остановиться сложно, тебя тянет на это дно. Рефакторинг смещает фокус внимания команды на себя, и мы получаем какой-то чудовищный перекос в сторону «наведения порядка» в уже написанном коде вместо того, чтобы двигать проект в светлое будущее. Так как распределить время?
Немного ответов дает следующий слайд:
Ведите логи времени, это дико полезно
Встречи и ревью остаются нетронутыми, потому что мы считаем, что эти активности максимально оптимизированы и сведены к адекватному минимуму (те, кто работал в командах, где митинги и ревью кода занимали 70% времени, наши слова подтвердят). Значит, мы берем время на рефакторинг кода и баг-фиксы из разработки. И тут мы опять применяем подход «одной и двух третей» и делим полученные нами человеко-часы на «рефакторинг» и «баги», четко разделяя эти понятия. Такая модель является жизнеспособной и позволяет найти время на наведение порядка на проекте, хотя бы не увеличивая технический долг. Если мы откусим от часов «разработки» слишком много, то проект застопорится.
Подходим к процессу рефакторинга правильно
Предположим, вы решили заняться рефакторингом вместо переписывания проекта с нуля. Но тут нельзя хвататься за первое попавшееся и рефакторить как в армии, «отсюда и до обеда». Так как наши ресурсы ограничены и наша цель — остановить рост технического долга (а в идеале добиться уменьшения его размеров), то и подходить к этой задаче надо здраво.
Лучшим решением кажется позволить тимлиду или другому руководителю разработки определить фронт работ, назначить таски и приступить к рефакторингу. Но у такого варианта есть серьезный изъян. Тимлид — это дирижер нашей команды, но дирижеру не всегда очевидны проблемы музыкантов на местах. Если вы сторонник жесткой вертикали и модели, в которой один человек будет решать, как будет проходить рефакторинг, то вы добровольно садитесь в горящий КаМАЗ, который со сломанными тормозами несется с горы в пропасть.
Наш опыт показывает, что здравый вариант развития событий — это коллективное определение тех или иных направлений рефакторинга, то есть в составлении списка грядущих работ должны принимать участие разработчики. Только непосредственный автор кода может честно признаться, был ли закрыт этот участок адекватно или из-за нехватки времени там нагромождено различных костылей. Кроме того именно разработчики находятся на острие технологий и способны здраво оценить, какие элементы проекта нужно рефакторить, а какие трогать не нужно.
Для себя мы придумали такой подход: каждый из разработчиков вспоминает тонкие места на проекте и пишет карточку, что нужно сделать, чтобы стало лучше. По этим карточкам потом будут определяться задачи. Правда, тут есть нюанс: после первой же полученной карточки в стиле «сделать хорошо» без какого-либо контекста, мы этот самый контекст в сжатом виде стали требовать.
Те самые карточки
Но определить задачу недостаточно для того, чтобы приступить к ее выполнению. Поэтому к каждой такой карточке должен составляться чек-лист, который отвечает на ряд простых вопросов вида «Нужно ли нам что-то делать параллельно?» или «Есть ли факторы, которые помешают нам выполнить эту задачу?» После заполнения подобного чек-листа разработчик получает конкретный список из всех возможных проблем и блокеров, которые надо решить на этапе подготовительных работ.
Еще один обязательный ход — определить, кто в каких участках проекта разбирается лучше всего. Это позволит оптимизировать процесс рефакторинга и обеспечить членов команды задачами на тех участках, где они лучше всего могут себя проявить. Правда, как-то раз мы столкнулись с ситуацией, что в одном из аспектов проекта нормально разбиралось полтора человека, но даже такая проблема решаема. Для устранения информационного вакуума разработчики, которые все же «шарят» в том участке, где остальная команда пасует, должны поделиться своими знаниями. Это может быть документация, какая-то мини-презентация, даже видео с пояснениями к коду. Важно донести информацию до коллег и сделать так, чтобы таких темных пятен было как можно меньше.
Ну и последнее: команда должна четко ответить на вопрос «каков по размерам наш технический долг?» Оценки вида «на троечку», «приемлемо», «пять из десяти», «жить можно» больше не работают. Раньше на подобный вопрос от СТО по одному из проектов мы давали ответ «пять из десяти», а когда перевели размер технического долга в цифры, то получили 650 часов. Было неловко. Только четкий ответ на этот вопрос поможет здраво оценить фронт предстоящих работ. Это важно и потому что «бесконечные» задачи убивают мотивацию членов команды, а также бесят бизнес: как разработчики, так и менеджмент должны видеть какую-то осязаемую конечную точку, к которой будет стремиться команда.
Расстановка приоритетов
После того, как мы определили фронт работ, нам нужно совместить рефакторинг с разработкой. Очевидно, мы не можем стопорить пиление фичей на разных участках проекта или выделять какое-то специальное время на это мероприятие.
Тут начинается, наверное, самое сложное. Команда разработки должна здраво определять, в каких моментах важнее разработка, а в каких — рефакторинг, особенно когда именно на этом участке кода прямо сейчас пилится какая-то фича. Возможно, после рефакторинга дальнейшая разработка пойдет проще, а в каких-то случаях мы можем закончить нашу фичу и только потом рефакторить. Мы общаемся с продуктом, аргументируем решение и договариваемся, каким будет порядок. Например: «Давай сначала порефакторим — не просто потому что нам так хочется, а потому что суммарный срок в итоге будет меньше».
Самое главное, это не забывать, что наша цель — это разработка продукта, а не написание идеального кода ради идеального кода или полное устранение технического долга. Разработка должна быть полностью адекватна в этом плане: если рефакторинг прямо сейчас вредит нашей бизнес-задаче по разработке, то мы заканчиваем писать участок нового кода, а только потом правим старый. Если обстоятельства говорят, что для проекта выгоднее будет провести рефакторинг этого участка (каким бы чудовищным не выглядел фронт работ), так как дальнейшая работа пойдет проще и веселее — то нам надо рефакторить.
Подобный подход крайне плохо вписывается в модель вертикального управления и иногда вы будете сталкиваться с ситуациями, когда вам придется отстаивать свои решения перед заказчиком. Но иначе никак.
Что мы получаем в итоге
С описанным подходом мы органично встраиваем процессы рефакторинга в текущую разработку, снижаем размер (ну или останавливаем рост) технического долга и все это без каких-либо крупных жертв.
Да, ради рефакторинга придется сократить время на разработку новых фич, что увеличит сроки, однако выбор тут невелик: вы либо рефакторите параллельно с разработкой, либо рано или поздно выходите на такое нагромождение костылей, проблем и багов, что проект будет проще переписать с нуля. Так что если вы цените труд своей команды, то стоит все же задуматься о рефакторинге вместо полного переписывания в будущем.
Полезные материалы:
1. Доклад Алексея Катаева про рефакторинг: больше примеров из практики, а также удивительная история менеджера Глеба, который не хотел рефакторить, пока…
2. Наш регламент обсуждения задач
3. Наш чек-лист код-ревью