Рефакторинг что это простыми словами
Цена рефакторинга
Вдохновители
Писать начал после очередного просмотра пулл-реквеста на 150+ файлов, где было люто замешана новая функциональность и рефакторинг существующей. Рефакторинг был не только косметическим, но и логическим, который вызывал наибольшую боль. Например, в Ruby amount == 0 смело заменялся на amount.zero? без учёта того, что для случая nil в amount эти конструкции не эквивалентны. Объяснялось же всё это примерно так: «но по код стандарту так положено!» На логичный вопрос «какая цель следования код стандарту и вообще, какая цель у тебя как разработчика?» человек замыкался в себе и немного виновато повторял «но по код-стандарту же вот так писать надо. » и выглядел как блондинка в магазине одежды, которая не в силах совладать с желанием отрефакторить купить всё.
Определение
Тема щекотливая, поэтому нужно особое внимание уделить вопросу «кто есть кто?» Так вот, согласно вики, рефакторинг — это процесс изменения внутренней структуры программы, не затрагивающий её внешнего поведения и имеющий целью облегчить понимание её работы.
Со своей стороны хочу сузить границы и определить рефакторинг (в худшем смысле это слова) как любые изменения, которые непосредственно не связаны с решаемой задачей и не изменяют внешнее поведение системы, но выполняются в рамках исходной задачи.
То есть поговорить хочу не о запланированном изменении кодовой базы, для которого очерчена область работ и поставлены конкретные цели, а о спонтанных модификациях, случающихся в ходе разработки.
Ценность продукта
Теперь начну издалека. Исходный код — не цель и не ценность. Конечно, эстетически или художественно он может представлять некоторый интерес, но это исключения. В общем случае, код является инструментом для создания программного продукта, которым кто-либо пользуется. Поэтому, для начала, хорошо было бы определить какие есть ценности в продукте.
Прямые ценности продукта
Тут всё просто. Продуктом пользуются, поэтому прямые ценности это то, что явно щупает/видит/чувствует пользователь. А именно:
Второй пункт может вызвать некоторую дискуссию. Ведь многие считают, что это не главное. Так как если функциональность хороша, то не важно во что она завёрнута. Хорошие примеры функциональности без вменяемого UI/UX: Redmine и SAP. Однако я с таким взглядом не согласен и ближе по взглядам к товарищу Алану Куперу и его «Психбольнице в руках пациентов».
Косвенные ценности продукта
Это те ценности, которые сами по себе на пользователя не влияют. Но могут «выстрелить» или «накопиться» и оказать влияние (разной степени тяжести) на продукт или его функциональность.
Ценности для разработчика
Очень важная категория, которую многие упускают из виду, но которая всегда влияет на результат.
Но давайте быть честными. Всё это не про ценности, связанные с продуктом и конечным пользователем. Это про психологию и личных тараканов.
Взгляд со стороны бизнеса
Для полноты картины надо бы посмотреть на всё это со стороны бизнеса, а не программного продукта. В этом случае, разделение на прямые и косвенные ценности становится довольно банальным: прямые — явно приносят деньги и можно дать однозначную количественную оценку; косвенные — денег не приносят и/или количественную оценку дать очень сложно; ценности для разработчика денег не приносят ни в каком виде (возможно, даже отнимают их).
Стоит отметить, что из вышеозначенных оценок растут ноги у любимого бизнесом ускорения работы, всеобщего поторапливания и нежелания выделять время на что-либо кроме функциональности и, быть может, багфикса. Ведь прямые ценности можно оценить и «продать», а косвенные — очень сложно, либо невозможно. И получается, что косвенные ценности осознаются либо в силу инженерного бэкграунда, либо понимаются на интуитивном уровне, либо выбрасываются как «бесполезные».
Справедливости ради, тут нужно вспомнить понятие enabler’а, который «открывает дорогу» к реализации желаемой фичи, но явного профита пользователю не несёт. Но это уже другая история.
Причём тут рефакторинг?
Как минимум при том, что он может влиять исключительно на косвенные ценности продукта. Поэтому ни от одного рефакторинга пользователю лучше не станет.
Так же, важно помнить про энтропию. Рефакторинг всегда неуменьшает её. Для уменьшения энтропии, в идеале, нужна команда архитекторов, минимизирующих энтропию на этапе проектирования. Процитирую кусочек из Мифического человекомесяца:
Системное программирование является процессом, уменьшающим энтропию, а потому ему внутренне присуща метастабильность. Сопровождение программ есть процесс, увеличивающий энтропию, и даже самое умелое его ведение лишь отдаляет впадение системы в безнадежное устаревание.
Посему, если считать рефакторинг частью процесса сопровождения программы, то чем меньше рефакторить, тем дольше код проживёт без переписывания.
Какие цели могут быть у рефакторинга?
Напомню, что говорю о спонтанном изменении в ходе реализации фичи, а не о запланированных изменениях. В этом случае определение цели полностью лежит на плечах разработчика. Он должен задать себе главный вопрос «зачем?», ответить на него и после этого, двигаться к намеченной цели.
За энтропию!
Такое самому провернуть сложно. А протащить через ревью так и вообще нереально. Цель конечно благая: расчистить плацдарм для новых свершений, а так же вычистить шлаки и токсины, накопленные за время поддержки продукта. Но перелопатить с нуля пару модулей не потеряв ничего по пути — это не самая простая задача. Её надо бы решать совместно с бизнес-аналитиками и архитекторами, если таковые есть. Так что таким мало кто рискует заниматься на добровольных началах.
За документацию!
Это уже проще. Переименовать переменные/функции по принципу «что на коробке, то и в коробке», упростить конструкции за счёт возможностей языка и библиотек, добавить комментариев в неочевидных местах и т.д. Такое можно сделать в одиночку и, при правильном подходе, можно сделать хорошо как себе будущему, так и коллегам соседним.
За быстродействие!
По-честному, такие работы должны быть выделены в отдельную задачу. Так как либо вам достаточно текущих возможностей системы и делать ничего не надо, либо вы знаете что конкретно и на сколько именно нужно ускорить.
В эту категорию входит всё: от безобидного исправления N+1 до серьёзного ускорения за счёт изменения алгоритмов работы. Вся проблема в том, что на скорости всегда сидит «чётность» багов. Вот, к случаю, пример из жизни: внутри одной транзакции пушатся данные в БД и в этой же транзакции ставится задача в Sidekiq; очередь Sidekiq’а в Redis’е и транзакция на него не распространяется; при увеличении скорости работы очереди таска иногда берётся на выполнение раньше, чем закоммитятся данные; последствия и процесс дебага можете представить сами.
За рефакторинг!
Представьте, что вы воспользовались сервисом по уборке квартиры. Они пришли, начали уборку, но, по ходу дела, всю мебель в квартире переставили и шторы из гостинной в ванну перевесили с аргументацией «в таких условиях уборщице было приятнее выполнять свою работу». Картинку с «WTF?!» можете домыслить сами.
Надеюсь, вы поняли, что думать нужно не о себе, а о том, для кого вы это делаете.
Смирение и принятие
В завершении, обязательно нужно дать «мануал» о том, что делать перед рефакторингом. Правда это не TODO-лист, а, скорее, список фактов, с которыми нужно смириться и, либо принять, либо не приступать к действу.
И ещё раз: если не согласны с хотя бы одним из пунктов — не начинайте рефакторинг. Лучше почитайте хабр, лурк, чаю выпейте или, на худой конец, запилите следующую фичу с дашборда.
Конец
Не судите строго. По возможности, критикуйте конструктивно. И всегда задумывайтесь над целью своих действий. Спасибо.
Что такое рефакторинг кода?
Что такое рефакторинг кода?
Концепция «рефакторинга» возникла в кругах, связанных со Smalltalk, но вскоре нашла себе дорогу и в лагеря приверженцев других языков программирования. Поскольку рефакторинг является составной частью разработки структуры приложений (framework development), этот термин сразу появляется, когда «структурщики» начинают обсуждать свои дела. Он возникает, когда они уточняют свои иерархии классов и восторгаются тем, на сколько строк им удалось сократить код. Структурщики знают, что хорошую структуру удается создать не сразу — она должна развиваться по мере накопления опыта. Им также известно, что чаще приходится читать и модифицировать код, а не писать новый. В основе поддержки читаемости и модифицируемости кода лежит рефакторинг — как в частном случае структур (frameworks), так и для программного обеспечения в целом.
Так в чем проблема? Только в том, что с рефакторингом связан известный риск. Он требует внести изменения в работающий код, что может привести к появлению трудно находимых ошибок в программе. Неправильно осуществляя рефакторинг, можно потерять дни и даже недели. Еще большим риском чреват рефакторинг, осуществляемый без формальностей или эпизодически. Вы начинаете копаться в коде. Вскоре обнаруживаются новые возможности модификации, и вы начинаете копать глубже. Чем больше вы копаете, тем больше вскрывается нового и тем больше изменений вы производите. В конце концов, получится яма, из которой вы не сможете выбраться. Чтобы не рыть самому себе могилу, следует производить рефакторинг на систематической основе. В книге «Design Patterns» сообщается, что проектные модели создают целевые объекты для рефакторинга. Однако указать цель — лишь одна часть задачи; преобразовать код так, чтобы достичь этой цели, — другая проблема.
Существует несколько методов рефакторинга. Каждый метод описывает мотивацию и технику испытанного на практике преобразования кода. Некоторые виды рефакторинга, такие как «Выделение метода» или «Перемещение поля», могут показаться очевидными, но пусть это не вводит вас в заблуждение. Понимание техники таких методов рефакторинга важно для организованного осуществления рефакторинга. С помощью методов рефакторинга можно поэтапно модифицировать код, внося каждый раз небольшие изменения, благодаря чему снижается риск, связанный с развитием проекта. Эти методы рефакторинга и их названия быстро займут место в вашем словаре разработчика.
Что такое рефакторинг?
Рефакторинг или реорганизация кода — процесс изменения внутренней структуры программного продукта, не затрагивающий её внешнего поведения и имеющий целью облегчение понимания программного кода и, пусть и не всегда, оптимизацию производительности.
В основе рефакторинга лежит последовательность небольших преобразований программного кода, сохраняющих его поведение. Так как каждое преобразование по объёму незначительно, то программисту легче проследить за его правильностью, а вся последовательность этих изменений может привести к существенной перестройке программы и улучшению её согласованности, четкости и простоты понимания её кода другими разработчиками.
«Улучшение кода после его написания» — непривычная фигура речи. В нашем сегодняшнем понимании разработки программного обеспечения мы сначала создаем дизайн системы, а потом пишем код. Сначала создается хороший дизайн, а затем происходит кодирование. Со временем код модифицируется, и целостность системы, соответствие ее структуры изначально созданному дизайну постепенно ухудшаются. Код медленно сползает от проектирования к хакерству.
Рефакторинг представляет собой противоположную практику. С ее помощью можно взять плохой проект, даже хаотический, и переделать его в хорошо спроектированный код. Каждый шаг этого процесса прост до чрезвычайности. Перемещается поле из одного класса в другой, изымается часть кода из метода и помещается в отдельный метод, какой-то код перемещается в иерархии в том или другом направлении. Однако суммарный эффект таких небольших изменений может радикально улучшить проект. Это прямо противоположно обычному явлению постепенного распада программы.
При проведении рефакторинга оказывается, что соотношение разных этапов работ изменяется. Проектирование непрерывно осуществляется во время разработки, а не выполняется целиком заранее. При реализации системы становится ясно, как можно улучшить ее проект. Происходящее взаимодействие приводит к созданию программы, качество проекта которой остается высоким по мере продолжения разработки.
Правила рефакторинга
Самый важный урок, который должен преподать данный пример, это ритм рефакторинга: тестирование, малые изменения, тестирование, малые изменения, тестирование, малые изменения. Именно такой ритм делает рефакторинг быстрым и надежным.
Принципы рефакторинга
Рефакторинг (Refactoring): изменение во внутренней структуре программного обеспечения, имеющее целью облегчить понимание его работы и упростить модификацию, не затрагивая наблюдаемого поведения.
Производить рефакторинг (Refactor): изменять структуру программного обеспечения, применяя ряд рефакторингов, не затрагивая его поведения.
Рефакторинг не меняет видимого поведения программного обеспечения. Оно продолжает выполнять прежние функции. Никто — ни конечный
Без рефакторинга не обходится ни один действительно сложный и долгоживущий проект
Обойтись без рефакторинга можно лишь в том случае, если разрабатывается что-то, что будет использовано лишь однажды, а потом просто выброшено. Во всех остальных случаях рефакторинг необходим. Проявляться он, конечно, может по разному.
В самом простом случае, рефакторинг осуществляется в процессе написания кода. Разработчик реализует функционал, добивается работоспособности, а затем проводит оптимизацию и рефакторинг написанного кода. К сожалению, сложно написать сложный программный компонент сразу идеально: полное понимание взаимосвязей, логики и вариантов реализации, как правило, приходит в процессе разработки.
Тем не менее, рефакторинга только в процессе разработки отдельных компонентов не достаточно. Если разрабатываемый компонент не изолирован, а взаимодействует с другими, то обычно есть необходимость в рефакторинге программных интерфейсов, через которые это самое взаимодействие реализуется.
В рамках всей программной системы перед рефакторингом стоит еще задача унификации именования функций и переменных, форматирования и достижения соблюдения прочих стандартов кодирования.
Наиболее частые причины для рефакторинга:
Когда следует проводить рефакторинг?
В жизни каждой программы, по крайней мере, в жизни тех, что разрабатываются на заказ, наступает этап, когда основные функциональные требования заказчика, по мнению разработчика, выполнены, и программный продукт поступает на тестирование. А может быть даже в опытную эксплуатацию. В ходе тестирования, если у того, кто его проводит, руки растут из нужного места и мозги работают в правильном направлении, на разработчика начинает валиться большое число bug-ов, связанных с исключительными ситуациями, “защитой от дурака”, экстремальными объемами данных, неприемлемым быстродействием и так далее (идеально работающие программы сразу не пишутся). Разработчик старается быстро реагировать на вызовы судьбы и вносит большое количество локальных исправлений, а иногда и “заплат”, вследствие чего код теряет первоначальную стройность и сбалансированность. Вот в моменты между основными волнами наплывов претензий со стороны отдела технического контроля или просто ОТК и следует проводить рефакторинг кода: анализировать код и, используя ряд эффективных приемов, преобразовывать его к более согласованному и “прозрачному виду.” Естественно, что этап рефакторинга нельзя считать однократным.
Также, уместно проводить рефакторинг кода после добавления новой функциональности, поскольку такие действия легко могут привести к необходимости провести ряд преобразований, связанных с манипуляцией классами и их элементами. Довольно часто новая функциональность является причиной извлечения новых методов или даже новых классов и/или переименования их, поскольку роль последних может быть расширена, уточнена или специализирована.
Как мне кажется, отдельного внимания, помимо прочих, должны быть удостоены и те части кода, которые давно не редактировались (не были затронуты в процессе исправления ошибок или расширения функциональности), поскольку вряд ли они настолько невосприимчивы к вносимым вами изменениям, хотя и сохраняют корректное поведение. Но это уже ответ скорее не на вопрос “Когда уместно…”, а на вопрос “Где искать…”
Почему рефакторинг приносит результаты
Из-за чего бывает трудно работать с программами? Есть, как минимум, 4 причины:
Итак, нам нужны программы, которые легко читать, вся логика которых задана в одном и только одном месте, модификация которых не ставит под угрозу существующие функции и которые позволяют выражать условную логику возможно более простым способом.
Рефакторинг представляет собой процесс улучшения работающей программы не путем изменения ее функций, а путем усиления в ней указанных качеств, позволяющих продолжить разработку с высокой скоростью.
Когда рефакторинг не нужен?
В некоторых случаях рефакторинг вообще не нужен. Основной пример — необходимость переписать программу с нуля. Иногда имеющийся код настолько запутан, что подвергнуть его рефакторингу, конечно, можно, но проще начать все с самого начала.
Явный признак необходимости переписать код — его неработоспособность. Это обнаруживается только при его тестировании, когда ошибок оказывается так много, что сделать код устойчивым не удается. Помните, что перед началом рефакторинга код должен выполняться в основном корректно.
Другой случай, когда следует воздерживаться от рефакторинга, это близость даты завершения проекта. Рост производительности, достигаемый благодаря рефакторингу, проявит себя слишком поздно — после истечения срока. Правильна в этом смысле точка зрения Уорда Каннингема (Ward Cunningham). Незавершенный рефакторинг он сравнивает с залезанием в долги. Большинству компаний для нормальной работы нужны кредиты. Однако вместе с долгами появляются и проценты, то есть дополнительная стоимость обслуживания и расширения, обусловленная чрезмерной сложностью кода. Выплату каких-то процентов можно вытерпеть, но если платежи слишком велики, вы разоритесь. Важно управлять своими долгами, выплачивая их часть посредством рефакторинга.
Однако приближение срока окончания работ — единственный случай, когда можно отложить рефакторинг, ссылаясь на недостаток времени. Опыт работы над несколькими проектами показывает, что проведение рефакторинга приводит к росту производительности труда. Нехватка времени обычно сигнализирует о необходимости рефакторинга.
Рефакторинг vs Реинжиниринг?
Если рефакторинг кода не меняет поведения программы, то реинжиниринг на такое способен. Можно рассматривать реинжиниринг, как крайнюю форму рефакторинга, но мне кажется, что это не вполне корректно. Скорее, рефактиринг может стать причиной легкого реинжиниринга. Рассмотрим вариант, когда периодический рефакторинг в процессе интенсивного тестирования программного продукта может привести к реинженирингу:
Специалисты ОТК, а возможно, уже и пользователи (зависит от этапа жизненного цикла программного продукта) постоянно регистрируют ошибки, непосредственно связанные с особенностью принятых вами решений при проектировании программы. Возможно, вы некорректно выбрали шаблон проектирования в процессе разработки архитектуры вашей программы. Инспекция кода и периодический рефакторинг могут выявить такие места – они выглядят громоздкими, нелогичными и не поддаются общеизвестным правилам рефакторинга. Вы принимаете решение внести изменения в архитектуру программы, оцениваете последствия и грядущие изменения в ее поведении, согласуете и объявляете о планируемых изменениях и приступаете к реинжинирингу.
Также, часто возникает потребность в реинжиниринге, когда необходимо расширить имеющуюся функциональность, или перейти на использование иных библиотек сторонних производителей, или на использование другой базы данных ну и т.д. и т.п.
Рефакторинг vs Оптимизация
Ну тут совсем все легко, потому как рефакторинг кода и оптимизация кода преследуют разные цели, и, как это обычно бывает, оказывают противоположный эффект на читаемость кода, хотя и тот и другой не меняют логику работы программы. В процессе оптимизации программного кода главной целью является обеспечение приемлемой скорости выполнения (или рационального расходования памяти в ходе выполнения) критических участков — “узких мест” в программе. В жертву могут быть принесены не только логичность и “красота” программного кода, но и основные догмы объектно-ориентированного подхода. Результатами оптимизации могут стать и появление глобальных переменных, и “лишние” отрытые методы и свойства, раскрывающие детали реализации ваших классов и т.п. безобразия. На мой взгляд, нужно уделять особое внимание рефакторингу, а необходимость в оптимизации стараться сводить к минимуму за счет уместного использования качественных библиотек сторонних производителей.
Разработка тестов
При проведении рефакторинга важным предварительным условием является наличие надежных тестов.
Правила разработки тестов
Признаки, что Вам нужен рефакторинга
Рефакторинг очень существенно влияет на сопровождаемость проекта
Любой проект без регулярного рефакторинга за несколько лет (или даже месяцев) становится трудным для понимания, процессы изменений замедляются и становятся дороже, а иногда такие проекты доходят до состояния «проще переписать тут всё с нуля, чем разбираться». Таким образом затраты на рефакторинг окупаются за счёт того, что изменения вносить становится проще и процесс модернизации обходится значительно дешевле.
Что такое рефакторинг
Как сделать код чище и понятнее.
В среде разработчиков можно услышать: «Мне нужен год на рефакторинг» (полгода, месяц, неделя, хотя бы денёк). Что это значит и зачем это нужно — разберём.
На примере кафе
Представим такую ситуацию: мы открыли кафе, построили там классную кухню и наняли шеф-повара. Когда кафе только запускалось, в меню были самые простые блюда, которые можно было разогреть в микроволновке. Вы купили микроволновку и поставили на кухне. Рядом разместили стеллаж для всего нужного.
Через два месяца дела пошли лучше, и мы решили добавить в меню выпечку. Купили духовой шкаф, протянули провода, добавили рядом стойку с подносами. Место на кухне уменьшилось, повар всё время перешагивает через провода и обходит стойку, но работать можно.
Ещё через месяц поставили фритюрницу и миксер для теста. К ним тоже протянули провода, добавили рядом шкафы для утвари и наняли второго повара. В итоге на кухне полный бардак, всё друг другу мешает, а блюда готовить неудобно. Если мы захотим добавить новую плиту, то это будет уже сложно: места нет, хотя по площади можно поставить хоть две таких плиты.
В этот момент приходит кухонный проектировщик и рисует всё заново: где что должно стоять.
Через неделю на кухне всё по-другому: оборудование стоит так, чтобы не мешать поварам, провода спрятаны в короба, а стойки с оборудованием не загораживают выход. При этом меню в кафе не изменилось: посетители даже не знают, что у нас что-то происходило на кухне, потому что мы оптимизировали процессы, а не меню. Это и есть рефакторинг — когда мы меняем что-то внутри так, что снаружи это незаметно, но работать дальше становится проще.
Когда нужен рефакторинг в программировании
Есть два подхода к рефакторингу: плановый и по необходимости.
Плановый рефакторинг означает, что разработчики сразу закладывают время на рефакторинг в цикл разработки. Например, каждые четыре спринта или каждые полгода.
В больших компаниях, где много легаси-кода, могут быть вообще отдельные команды, которые занимаются только рефакторингом старья. Это помогает остальным командам быстрее понимать, что в нём происходит и как им пользоваться.
Второй подход — рефакторинг по необходимости, когда добавление новых возможностей тормозится из-за того, что их сложно интегрировать в старый код. Тогда мы говорим «Стоп машина» и берём какое-то время на реорганизацию всего, что было.
На что смотрят при рефакторинге кода
Главный показатель успешного рефакторинга — после него код стал чище, проще и понятнее.
Например, если переменная Z в программе отвечает за количество покупателей, то лучше её заменить на customerCount — так будет проще разобраться в коде и понять, что там происходит.
Если фрагмент кода повторяется больше одного раза, то его чаще всего выносят в отдельную функцию или метод. В этом случае будет легче заменить код в одном месте, чем искать повторяющиеся фрагменты по всей программе.
Ещё программисты обращают внимание на размер функций, методов и классов. Если функция получается слишком большой, чтобы поместиться на одном экране, — её разбивают на две, чтобы упростить читаемость кода.
Иногда, чтобы сделать код проще, разработчики выносят часть функций в отдельный файл и подключают его к основной программе.
А можно без рефакторинга?
Жить можно и без рефакторинга, но чем дальше без него — тем тяжелее работать. Рефакторинг — это как наведение порядка на рабочем месте. Если долго им не заниматься, со временем работать становится неудобно. Регулярный рефакторинг помогает не замедлять дальнейшую разработку в больших командах.
Без рефакторинга можно только в маленьких продуктах, которые развиваются медленно.
Что дальше
В следующей статье попробуем применить эти знания на практике и отрефакторить какой-нибудь из наших старых проектов, чтобы посмотреть, как изменится код и что получится в итоге.