Что такое рефакторинг и как им воспользоваться
Что такое рефакторинг
Как сделать код чище и понятнее.
В среде разработчиков можно услышать: «Мне нужен год на рефакторинг» (полгода, месяц, неделя, хотя бы денёк). Что это значит и зачем это нужно — разберём.
На примере кафе
Представим такую ситуацию: мы открыли кафе, построили там классную кухню и наняли шеф-повара. Когда кафе только запускалось, в меню были самые простые блюда, которые можно было разогреть в микроволновке. Вы купили микроволновку и поставили на кухне. Рядом разместили стеллаж для всего нужного.
Через два месяца дела пошли лучше, и мы решили добавить в меню выпечку. Купили духовой шкаф, протянули провода, добавили рядом стойку с подносами. Место на кухне уменьшилось, повар всё время перешагивает через провода и обходит стойку, но работать можно.
Ещё через месяц поставили фритюрницу и миксер для теста. К ним тоже протянули провода, добавили рядом шкафы для утвари и наняли второго повара. В итоге на кухне полный бардак, всё друг другу мешает, а блюда готовить неудобно. Если мы захотим добавить новую плиту, то это будет уже сложно: места нет, хотя по площади можно поставить хоть две таких плиты.
В этот момент приходит кухонный проектировщик и рисует всё заново: где что должно стоять.
Через неделю на кухне всё по-другому: оборудование стоит так, чтобы не мешать поварам, провода спрятаны в короба, а стойки с оборудованием не загораживают выход. При этом меню в кафе не изменилось: посетители даже не знают, что у нас что-то происходило на кухне, потому что мы оптимизировали процессы, а не меню. Это и есть рефакторинг — когда мы меняем что-то внутри так, что снаружи это незаметно, но работать дальше становится проще.
Когда нужен рефакторинг в программировании
Есть два подхода к рефакторингу: плановый и по необходимости.
Плановый рефакторинг означает, что разработчики сразу закладывают время на рефакторинг в цикл разработки. Например, каждые четыре спринта или каждые полгода.
В больших компаниях, где много легаси-кода, могут быть вообще отдельные команды, которые занимаются только рефакторингом старья. Это помогает остальным командам быстрее понимать, что в нём происходит и как им пользоваться.
Второй подход — рефакторинг по необходимости, когда добавление новых возможностей тормозится из-за того, что их сложно интегрировать в старый код. Тогда мы говорим «Стоп машина» и берём какое-то время на реорганизацию всего, что было.
На что смотрят при рефакторинге кода
Главный показатель успешного рефакторинга — после него код стал чище, проще и понятнее.
Например, если переменная Z в программе отвечает за количество покупателей, то лучше её заменить на customerCount — так будет проще разобраться в коде и понять, что там происходит.
Если фрагмент кода повторяется больше одного раза, то его чаще всего выносят в отдельную функцию или метод. В этом случае будет легче заменить код в одном месте, чем искать повторяющиеся фрагменты по всей программе.
Ещё программисты обращают внимание на размер функций, методов и классов. Если функция получается слишком большой, чтобы поместиться на одном экране, — её разбивают на две, чтобы упростить читаемость кода.
Иногда, чтобы сделать код проще, разработчики выносят часть функций в отдельный файл и подключают его к основной программе.
А можно без рефакторинга?
Жить можно и без рефакторинга, но чем дальше без него — тем тяжелее работать. Рефакторинг — это как наведение порядка на рабочем месте. Если долго им не заниматься, со временем работать становится неудобно. Регулярный рефакторинг помогает не замедлять дальнейшую разработку в больших командах.
Без рефакторинга можно только в маленьких продуктах, которые развиваются медленно.
Что дальше
В следующей статье попробуем применить эти знания на практике и отрефакторить какой-нибудь из наших старых проектов, чтобы посмотреть, как изменится код и что получится в итоге.
Рефакторинг — мощь сокрытая в качественном коде
Рефакторинг — это крайне важная часть процесса разработки. Писать код «который работает» не есть залогом производительного приложения. Рефакторинг дает возможность привести код в надлежащий вид, что позволит в дальнейшем этот код легко читать, использовать повторно, поддерживать и расширять саму систему.
Проектирование
Начало хорошего кода — это всегда проектирование. Программисты, которые не умеют утихомирить страсть к написанию кода, этим опуская проектирование, пишут обычно быстро, но не качественно. Я это знаю, так как сам имел ту же проблему. Проектирование дает возможность взглянуть на систему, которой еще фактически нету, продумать правильную структуру приложения и данных, увидеть тонкости, риски, подумать о производительности и безопасности. При этом проектирование это не только прерогатива начала проекта. Проектирование — это неотъемлемая часть, при разработке любой «Feature».
Начать проектировать можно очень легко. Всегда держите на рабочем месте блокнот и несколько цветов ручек. Прежде чем писать код, нарисуйте схему — как приложение будет работать в целом, UML-диаграмму классов (продумайте как можно с минимальным количеством классов, достигнуть максимального результата), структуру баз данных (оптимизируйте БД еще до ее создания, подумайте какие запросы у вас должны будут «бегать» к вашей БД, продумайте индексы, конечно же нормализируйте вашу модель данных).
Для тех же целей подойдет простая программа для проектирования starUML. Минус ее в том, что нельзя нормально устанавливать мощность отношений (кратность), но сам интерфейс очень удобный.
Проектировать нужно даже для того, чтобы потом не пришлось рефакторить структурные ошибки, а еще хуже переписывать вообще весь модуль.
Несколько принципов, которые точно нужно знать при проектировании классов вашей «Feature»:
1. SOLID (single responsibility, open-closed, Liskov substitution, interface segregation и dependency inversion)
Это основа основ в проектировании классов. Если вы еще не знакомы с SOLID, здесь можно ознакомиться.
2. DRY (do not repeat yourself)
Повторяющейся функционал делает приложение громоздким, а его поддержку более дорогостоящей и неудобной. Это относится как к модулям, так и к небольшим фрагментам кода.
— Вместо того, чтобы использовать три строчки кода в нескольких местах, можно поместить их в функцию и использовать всего лишь одну строку кода — вызов функции.
— Вместо использования функции progress50(), лучше применить более абстрактную progress($percent).
— Отдавать предпочтение внешним зависимостям между модулями, внутренним (DI), что делает модуль более гибким и позволяет его использовать в нескольких местах.
3. KISS (keep it simple, stup. )
Чем проще ваше решение для сложной задачи, тем более оно совершенно, так же это правило работает в обратную сторону. Чтобы научиться делать простые решения, нужно научиться разделять вашу задачу на очень маленькие подзадачи. Двигаясь от меньшего к большему. В последствии, мы получаем результат сложной задачи.
Вам нужно написать класс для генерации Excel отчета с вашей БД. Нужно весь отчет разделить на части: установка заголовков таблицы, вывод статистических данных в документ, вывод подвала документа, создание диаграммы. Некоторые части функционала можно вынести в отдельные классы, что даст возможность использовать их повторно.
Стиль кода
Рефакторинг стиля кода вообще не влияет на производительность приложения. Тем не менее, он дает преимущество для его читаемости и поддержки. Другой программист должен легко читать ваш код, без вникания в реализацию деталей, а это, конечно же, экономит время и деньги.
Стандарт стиля кода (и не только) PSR (PHP Standards Recommendations), здесь можно ознакомиться. Содержимое на английском языке, так как английский более ясно дает понять степень применения одного или другого правила («MUST», «MUST NOT», «REQUIRED», «SHALL», «SHALL NOT», «SHOULD», «SHOULD NOT», «RECOMMENDED», «MAY», and «OPTIONAL»).
Несколько замечаний, которые автор счел важными:
1. Ознакомьтесь с PHPDOC для написания комментариев к вашему коду.
2. Лучший комментарий — это правильно названный класс, метод, параметр или переменная.
3. Используйте утилиты PHPMD, PHPCS, их применение шире, чем только для определения несоответствий в стиле кода. Вот документация: PHPMD, PHPCS.
4. Используйте продвинутое IDE.
Рефакторинг в чистом виде
Очень простая аксиома — на продакшн должен попадать только код, прошедший рефакторинг. Иногда после разработки вы сами делаете рефакторинг, что очень даже не плохо (к примеру, разработка через тестирование вообще включает рефакторинг, как обязательный шаг, так как изначально пишется «работающий код», а потом уже «чистый»), но для того, чтобы код был по-настоящему качественным, он должен пройти проверку кода (code-review) другим программистом. Если проект позволяет выделить время на проверку кода, то на таком проекте ты будешь учиться писать код чище и чище, что в последствии приведет к автоматическому написанию качественного кода.
Я помню, как работал на одну компанию, и проверку кода у меня делал человек, который не всегда мог сдерживать свои эмоции, из-за чего о моем не чистом коде слышали все в open space. Поэтому мне приходилось учиться писать качественный код, очень быстро, чтобы избежать подобных случаев.
Вернемся к теме. В этой части статьи я бы хотел дать несколько практических подходов к рефакторингу, которыми пользуюсь сам.
1. Длинные методы (лучше разделить функционал на несколько методов).
2. Громоздкие классы (ваш класс должен исполнять одну функциональную задачу в вашей системе).
3. Неясная структура класса (методы в хаотическом порядке, конструктор в середине класса, вместо констант — магические значения в коде — класс должен легко отображать, что он делает в правильной последовательности).
4. Слишком много параметров в методе (некоторые расчеты можно сделать внутри метода, используя внутренние константы, значения полученные с атрибутов и геттеров).
5. Классы, содержащие одинаковые переменные и методы. Проблему можно решить через создание дополнительного класса).
6. Сложно читаемый IF (выражение можно вынести в отдельную переменную и разделить на логические части, которые также вынести в переменные, если много проверок на null, то лучше всего использовать NullObject — количество проверок значительно уменьшится).
7. Громоздкий SWITH (выносим в отдельный метод).
8. Использование наследования из-за одинаковых методов и свойств, в разных по своей сути сущностях (кошка и стул имеют ноги, но их нельзя группировать в категорию «животные»).
9. Слишком большое количество маленьких классов для выполнения одной задачи, которые в последствии сложнее поддерживать.
10. Слишком сложный функционал в одном классе, который можно разделить на несколько классов.
11. Класс делает слишком мало, чтобы его оставлять в системе.
12. «Мертвый код» — его следует удалить.
13. Не использованные структуры классов, которые вы проектировали на будущее, но они так и не пригодились — такие лучше удалить.
14. Методы класса больше используются в другом классе, а в своем вообще не используются или же реже (стоит перенести метод в тот класс, где он больше используется).
15. Слишком длинная цепочка вызовов ($a->b()->c()->d()->e()), в этом случае стоит создать дополнительные методы.
16. Класс, содержащий только один метод, который создает другой класс. (Такой класс нужно использовать с умом, к примеру, для паттерна «Прокси», в противном случае этот класс только увеличивает время и ресурс на поддержку проекта).
Что такое рефакторинг кода
Зачем разработчики на регулярной основе переписывают свой и чужой код, не добавляя туда ни одной новой функции и не исправляя ни одной ошибки?
Сейчас все расскажу. Поговорим о чудесной процедуре рефакторинга, спасающей тысячи программистов от бессонных ночей и психологических травм.
Что такое рефакторинг
Под рефакторингом подразумевается переработка уже существующего кода с целью упростить его. Упростить не с функциональной точки зрения, чтобы увеличить производительность ПО и сократить количество потенциальных ошибок, а с точки зрения визуального восприятия. Проще говоря, рефакторинг – превращение нагромождения кода в что-то удобоваримое и более читаемое, чтобы другие программисты не мучались в попытках понять, какая функция к чему относится, как с ней работать, какой результат она выдает и т.п.
Рефакторинг позволяет приблизиться к четкому соблюдению одного из важнейших правил написания кода – он должен быть «красивым» и лаконичным.
Определение Мартина Фаулера
Вопрос «Что такое рефакторинг?» часто возникает у программистов-новичков, а иногда и у более опытных разработчиков. Поэтому он регулярно всплывает на форумах в духе StackOverflow.
Там в качестве ответа на вопрос приводят цитату из книги «Refactoring: Improving the Design of Existing Code» за авторством Мартина Фаулера:
Рефакторинг – это контролируемая техника совершенствования структуры существующего кода. Суть рефакторинга заключается во внесении серии мелких изменения (с сохранением функциональности приложения), каждое из которых «слишком мелкое, чтобы тратить на него время». Тем не менее эффект от внесения всех этих изменений достаточно ощутимый.
Также в этой книге рекомендуется выполнять рефакторинг пошагово, чтобы исключить появление ошибок. А пользователи StackOverflow советуют каждое изменение сопровождать применением юнит-тестов. И хотя многие отрицают столь тесную связь этих двух операций, большинство опытных кодеров все же не упускают возможности задействовать тесты на любом из этапов разработки или модификации ПО.
Что не является рефакторингом?
Те, кто не углубляется в изучение терминологии, часто принимает за рефакторинг целую серию других действий, отчасти похожих на рефакторинг. В их числе:
простое переписывание кода,
улучшения функциональной составляющей ПО,
Первое имеет смысл при создании нового ПО или самотренировок.
Второе подразумевает поиск ошибок и их устранение, сам код при этом необязательно должен становиться проще или понятнее для других разработчиков. Цель дебаггинга – заставить программу работать корректно, не наплодив при этом новых ошибок.
Третье может быть связано с модификацией «читаемости» кода, но это необязательная составляющая. Важно сделать ПО лучше с пользовательской точки зрения, а не с точки зрения разработчика.
Четвертый термин чаще всего путают с рефакторингом, потому что они как раз иногда выполняются параллельно, но оптимизация – фокус на производительности программы. Код может стать даже сложнее, но ПО должно работать шустрее.
Но ни что из перечисленного выше не является рефакторингом, и зачастую каждая из процедур выполняется отдельно (за редким исключением, когда некорректное поведение ПО вызвано неправильно написанным кодом).
Зачем нужен рефакторинг?
Есть гласное правило для всех программистов – код должен быть лаконичным, хорошо структурированным и понятным для разработчиков, работающих с ним. Проблема в том, что написать такой код с первого раза – очень сложная задача. Каким бы опытным ни был программист, начальство заставит его спешить, заказчики будут менять требования по ходу разработки, а иногда код будет становиться непонятным из-за банального недосыпа. Более того, сами языки программирования регулярно совершенствуются и обретают новые возможности, позволяя заметно сократить количество кода. Поэтому и нужен рефакторинг.
Ожидаемые преимущества рефакторинга:
Улучшение объективной читаемости кода за счет его сокращения или реструктуризации.
Провоцирование разработчиков на более вдумчивое написание ПО с соблюдением заданной стилистики и критериев.
Подготовка плацдарма для создания переиспользуемых кусков кода.
Упрощение поиска ошибок в большом объеме кода.
Грамотно выполненный рефакторинг кода позволяет «продлить» жизнь проекту и сделать легче трудовую деятельность программистов из будущего.
В каких случаях нужен рефакторинг?
Есть ряд ситуаций, которые «кричат» о необходимости рефакторинга:
Попытка внести любое улучшение или добавление новой функции в приложение превращается в проблему для разработчиков, а сроки выполнения, на первый взгляд, несложных операций затягиваются на неадекватный срок из-за того, что база кода напоминает дебри.
Сроки добавления новых функций в приложение на постоянной основе становятся размытыми, потому что разработчикам приходится закладывать время на анализ кода.
Приходится выполнять идентичные процедуры в разных участках кода (объектах, классах) вместо того, чтобы внести изменение в одном классе, и оно возымело бы эффект в других участках ПО.
Код не соответствует общепризнанным в компании практикам оформления, из-за чего не может использоваться для дальнейшей разработки с учетом ранее установленных требований.
Впрочем, есть и другие, более индивидуальные факторы, подвигающие команду программистов на рефакторинг кода. Это зависит и от особенностей работы в конкретной компании, где серьезным поводом для рефакторинга может считаться даже неправильное количество пробелов в начале строки.
На какие аспекты кода направлен рефакторинг?
Четкой структуры рефакторинга не существует. Можно вносить любые изменения, улучшающие читаемость кода. Но обычно в поле зрения сразу же попадают следующие представители «плохого» кода.
Неиспользуемый код
В коде часто остается мусор в духе незадействованных переменных или методов. В базе кода висит текст, никак не влияющий на работу приложения, и его нужно удалить, чтобы не создавать путаницу. В этом, кстати, помогают современные тестовые редакторы, например VS Code.
Дубликаты
При длительной разработке сложного ПО можно замешкать и наплодить одинаковых функций или переменных. А еще в объектах могут существовать идентичные методы, но описанные в каждом отдельно. Такой код лучше вынести в родительский класс.
Переменные и функции названы неадекватно
К программистам предъявляются требования по оформлению кода, одно из ключевых – необходимость давать понятные имена переменным и функциям, чтобы без дополнительных комментариев можно было понять, что делает тот или иной метод. Никаких букв и случайных наборов символов.
Код, из которого невозможно что-либо понять
Избыточное количества текста в одном методе
Лучше поделить функцию на несколько составных частей, чем создавать одну слишком большую и трудночитаемую. Если ваша функция состоит из 70 строк кода – это не норма. Это же касается классов и других объектов.
Вот так может выглядеть функция преобразования текста в массиве
Та же функция, но описанная одной строкой
Избыточное количество комментариев
Если приходится пояснять каждую строчку кода, то с кодом что-то не так. Программист, который видит ваш код первый раз, должен разобраться в нем быстро, без необходимости начитывать целые тома комментариев и документации.
Некорректно оформленные куски кода
У кода есть правила визуального оформления. Нужно соблюсти корректное число пробелов от начала строки, правильно «вкладывать» одни компоненты в другие, соблюдать правила написания функций и циклов.
Для решения этих задач можно использовать специальные плагины. Расширение ESLint поможет писать красивый код, соответствующий общепринятым стандартам, а плагин Prettier самостоятельно расставит все запятые, пробелы и т.п., чтобы код отлично смотрелся в любом текстовом редакторе.
Методики рефакторинга
Разработчики и специалисты в области рефакторинга часто называют десятки различных тактик переработки кода, но почти все они четко привязаны к изменяемому компоненту (объекту, функции и т.п.), поэтому нет смысла их все перечислять. Обобщая, есть три основных способа выполнить рефакторинг:
Абстракция. Эта стратегия используется, когда нужно почистить дубликаты. Разработчиками придумываются абстрактные классы или другие классы высокого уровня, чтобы вынести туда повторяющиеся методы.
Фрагментация. Стратегия изменения кода с целью увеличить количество компонентов, но сделать сами компоненты меньше. Что-то в духе методик планирования задач, часто применяемых для повышения личной эффективности.
Сложности рефакторинга
Менять рабочий код всегда опасно. Даже мелкие изменения, кажущиеся суперлогичными и неопасными, иногда ломают приложение. Из-за этого рефакторинг и сопровождают тестами, потому что некоторые горе-программисты без особого внимания к деталям переписывают целые классы, а потом не могут включить ПО, потому что оно больше не работает.
Опытные разработчики рекомендуют следующие практики:
Делать рефакторинг как рутину. Не раз в полгода, а регулярно, но по чуть-чуть. Тогда он будет отнимать меньше времени и не будет отвлекать от более важных задач.
Не делать рефакторинг священной коровой, из-за которой откладываются более важные задачи в духе внедрения новых функций или дебаггинга.
Не пренебрегать рефакторингом, даже если трудитесь над всей базой кода самостоятельно. Если вы что-нибудь со временем забудете, то устанете разбираться в собственном коде. Не зря про это сочинили сотни однотипных шуток еще во времена bash.org.
Вместо заключения
Как видите, рефакторинг – это хоть и простое явление с точки зрения идеи, но необходимое для избежания задержек в разработке и сохранения нервных клеток коллег. Главное – сопровождайте каждый значимый этап рефакторинга тестами, чтобы сохранить «перерабатываемый» код в рабочем состоянии. Также стоит использовать системы контроля версий, каждое новшество отправляя отдельным коммитом в хранилище наподобие GitHub или GitLab. Это поможет в случае чего «откатить» неаккуратный рефакторинг и попытаться снова. Ведь самый понятный и читаемый в мире код все еще должен выполнять свои задачи, а не просто радовать взгляд искушенных кодеров.
Обучиться рефакторингу можно на курсах по программированию общего назначения у EPAM и Hexlet, а также на узкоспециализированных ресурсах в духе Refactoring Guru.
Рефакторим параллельно с разработкой: наш опыт и два чек-листа
Для множества команд рефакторинг — это боль. Потому что если ты занимаешься рефакторингом, то не разрабатываешь основной продукт, а если не занимаешься — растет технический долг проекта. В какой-то момент команды приходят к мысли: «Давайте разграничим рефакторинг и разработку и выделим на него, например, 20% наших человеко-часов, а остальное время продолжим заниматься разработкой!» Мысль эта неплохая, вот только дело в том, что на каждые 100 разработка-часов вы никогда не получите 20 чистых часа рефакторинга. Потому что «разработка» — это не только работа с кодом.
Если мы говорим о зрелых командах, а не сжатых в материальную точку мини-коллективах на 3-5 человек, то «разработка» включает в себя еще целую массу различных активностей команды кроме написания кода. В «разработку» можно записать так нелюбимые многими митинги, работу с документацией, ведение отчетности в таск-менеджерах и так далее. Все это съедает примерно 30% от наших часов, выделенных на разработку. И как-то незаметно у нас получается, что вместо картины «80 часов кодим, 20 часов рефакторим» мы получаем неприглядную цифру в
13 часов на, непосредственно, сам рефакторинг, потому что все остальное было поглощено другими активностями.
Ниже мы расскажем, как все же совместить разработку с рефакторингом так, чтобы технический долг проекта не рос, будто снежный ком, а еще поговорим о правильном распределении времени и расстановке приоритетов.
Почему вообще рефакторинг вызывает столько проблем? В первую очередь по причине того, что команды не могут адекватно оценить собственные ресурсы и совместить этот процесс с разработкой. Людям кажется, что если их рабочий день составляет 8 часов в день (40 в неделю), то все 40 часов они, якобы, занимаются разработкой. Это не так.
Весь процесс разработки можно разделить на две части: это побочные активности и все, что связано непосредственно с кодом.
Вот как выглядит распределение времени одной из наших команд разработки
У читателя может возникнуть резонный вопрос: «а как вы построили эту диаграмму?» и сейчас мы постараемся дать ответ. Данные были взяты не с потолка, а на основе нашей внутренней статистики. Наши разработчики ведут учет своей активности в Jira. Каждый такой персональный ворклог состоит из четырех разделов: код-ревью, технические обсуждения, конкретные тикеты и «все остальное». Благодаря этой достаточно простой и прозрачной классификации мы и построили аналитику того, сколько времени в каких командах мы тратим на каждый аспект разработки. Если на встречи, стендапы и ревью выпадает треть всего рабочего времени — все в пределах нормы. Если времени на эти активности тратится больше 33% — то в команде есть проблемы и их надо решать.
Вроде бы, тут нет подвоха и все логично, но как нам вписать в эту историю рефакторинг? Объявить «месяц рефакторинга» и забить на разработку? Однако у любого коммерческого продукта есть свой график, выбиваться из которого крайне нежелательно. Еще рефакторинг похож на трясину: если начал им заниматься, то остановиться сложно, тебя тянет на это дно. Рефакторинг смещает фокус внимания команды на себя, и мы получаем какой-то чудовищный перекос в сторону «наведения порядка» в уже написанном коде вместо того, чтобы двигать проект в светлое будущее. Так как распределить время?
Немного ответов дает следующий слайд:
Ведите логи времени, это дико полезно
Встречи и ревью остаются нетронутыми, потому что мы считаем, что эти активности максимально оптимизированы и сведены к адекватному минимуму (те, кто работал в командах, где митинги и ревью кода занимали 70% времени, наши слова подтвердят). Значит, мы берем время на рефакторинг кода и баг-фиксы из разработки. И тут мы опять применяем подход «одной и двух третей» и делим полученные нами человеко-часы на «рефакторинг» и «баги», четко разделяя эти понятия. Такая модель является жизнеспособной и позволяет найти время на наведение порядка на проекте, хотя бы не увеличивая технический долг. Если мы откусим от часов «разработки» слишком много, то проект застопорится.
Подходим к процессу рефакторинга правильно
Предположим, вы решили заняться рефакторингом вместо переписывания проекта с нуля. Но тут нельзя хвататься за первое попавшееся и рефакторить как в армии, «отсюда и до обеда». Так как наши ресурсы ограничены и наша цель — остановить рост технического долга (а в идеале добиться уменьшения его размеров), то и подходить к этой задаче надо здраво.
Лучшим решением кажется позволить тимлиду или другому руководителю разработки определить фронт работ, назначить таски и приступить к рефакторингу. Но у такого варианта есть серьезный изъян. Тимлид — это дирижер нашей команды, но дирижеру не всегда очевидны проблемы музыкантов на местах. Если вы сторонник жесткой вертикали и модели, в которой один человек будет решать, как будет проходить рефакторинг, то вы добровольно садитесь в горящий КаМАЗ, который со сломанными тормозами несется с горы в пропасть.
Наш опыт показывает, что здравый вариант развития событий — это коллективное определение тех или иных направлений рефакторинга, то есть в составлении списка грядущих работ должны принимать участие разработчики. Только непосредственный автор кода может честно признаться, был ли закрыт этот участок адекватно или из-за нехватки времени там нагромождено различных костылей. Кроме того именно разработчики находятся на острие технологий и способны здраво оценить, какие элементы проекта нужно рефакторить, а какие трогать не нужно.
Для себя мы придумали такой подход: каждый из разработчиков вспоминает тонкие места на проекте и пишет карточку, что нужно сделать, чтобы стало лучше. По этим карточкам потом будут определяться задачи. Правда, тут есть нюанс: после первой же полученной карточки в стиле «сделать хорошо» без какого-либо контекста, мы этот самый контекст в сжатом виде стали требовать.
Те самые карточки
Но определить задачу недостаточно для того, чтобы приступить к ее выполнению. Поэтому к каждой такой карточке должен составляться чек-лист, который отвечает на ряд простых вопросов вида «Нужно ли нам что-то делать параллельно?» или «Есть ли факторы, которые помешают нам выполнить эту задачу?» После заполнения подобного чек-листа разработчик получает конкретный список из всех возможных проблем и блокеров, которые надо решить на этапе подготовительных работ.
Еще один обязательный ход — определить, кто в каких участках проекта разбирается лучше всего. Это позволит оптимизировать процесс рефакторинга и обеспечить членов команды задачами на тех участках, где они лучше всего могут себя проявить. Правда, как-то раз мы столкнулись с ситуацией, что в одном из аспектов проекта нормально разбиралось полтора человека, но даже такая проблема решаема. Для устранения информационного вакуума разработчики, которые все же «шарят» в том участке, где остальная команда пасует, должны поделиться своими знаниями. Это может быть документация, какая-то мини-презентация, даже видео с пояснениями к коду. Важно донести информацию до коллег и сделать так, чтобы таких темных пятен было как можно меньше.
Ну и последнее: команда должна четко ответить на вопрос «каков по размерам наш технический долг?» Оценки вида «на троечку», «приемлемо», «пять из десяти», «жить можно» больше не работают. Раньше на подобный вопрос от СТО по одному из проектов мы давали ответ «пять из десяти», а когда перевели размер технического долга в цифры, то получили 650 часов. Было неловко. Только четкий ответ на этот вопрос поможет здраво оценить фронт предстоящих работ. Это важно и потому что «бесконечные» задачи убивают мотивацию членов команды, а также бесят бизнес: как разработчики, так и менеджмент должны видеть какую-то осязаемую конечную точку, к которой будет стремиться команда.
Расстановка приоритетов
После того, как мы определили фронт работ, нам нужно совместить рефакторинг с разработкой. Очевидно, мы не можем стопорить пиление фичей на разных участках проекта или выделять какое-то специальное время на это мероприятие.
Тут начинается, наверное, самое сложное. Команда разработки должна здраво определять, в каких моментах важнее разработка, а в каких — рефакторинг, особенно когда именно на этом участке кода прямо сейчас пилится какая-то фича. Возможно, после рефакторинга дальнейшая разработка пойдет проще, а в каких-то случаях мы можем закончить нашу фичу и только потом рефакторить. Мы общаемся с продуктом, аргументируем решение и договариваемся, каким будет порядок. Например: «Давай сначала порефакторим — не просто потому что нам так хочется, а потому что суммарный срок в итоге будет меньше».
Самое главное, это не забывать, что наша цель — это разработка продукта, а не написание идеального кода ради идеального кода или полное устранение технического долга. Разработка должна быть полностью адекватна в этом плане: если рефакторинг прямо сейчас вредит нашей бизнес-задаче по разработке, то мы заканчиваем писать участок нового кода, а только потом правим старый. Если обстоятельства говорят, что для проекта выгоднее будет провести рефакторинг этого участка (каким бы чудовищным не выглядел фронт работ), так как дальнейшая работа пойдет проще и веселее — то нам надо рефакторить.
Подобный подход крайне плохо вписывается в модель вертикального управления и иногда вы будете сталкиваться с ситуациями, когда вам придется отстаивать свои решения перед заказчиком. Но иначе никак.
Что мы получаем в итоге
С описанным подходом мы органично встраиваем процессы рефакторинга в текущую разработку, снижаем размер (ну или останавливаем рост) технического долга и все это без каких-либо крупных жертв.
Да, ради рефакторинга придется сократить время на разработку новых фич, что увеличит сроки, однако выбор тут невелик: вы либо рефакторите параллельно с разработкой, либо рано или поздно выходите на такое нагромождение костылей, проблем и багов, что проект будет проще переписать с нуля. Так что если вы цените труд своей команды, то стоит все же задуматься о рефакторинге вместо полного переписывания в будущем.
1. Доклад Алексея Катаева про рефакторинг: больше примеров из практики, а также удивительная история менеджера Глеба, который не хотел рефакторить, пока…