2.19 Рефакторинг и технический долг
Обзор и мотивация
Рефакторинг это изменение внутренней структуры кода без изменения того, что он делает снаружи. Вы переименовываете переменную, разбиваете длинную функцию, выделяете класс, сворачиваете путаницу условий во что-то, за чем читатель может следить, и программа ведёт себя ровно как раньше. Эта последняя часть и составляет всю дисциплину. Рефакторинг по определению сохраняет поведение, а в момент, когда вы заодно меняете поведение, вы больше не рефакторите, вы делаете две рискованные вещи сразу и прячете каждую за другой. Эта глава намеренно рассматривает их как отдельные акты, потому что путаница между ними то место, где большинство рефакторинга идёт не так.
В большой команде это важнее, чем для одиночного разработчика, потому что код, который вы приводите в порядок, это код, который читают, от которого зависят и которого боятся касаться сотни других людей. Рефакторинг это то, как общая кодовая база остаётся обитаемой годами и сменой персонала. Он напрямую связан с конструированием программного обеспечения (глава 2.9), где задаётся повседневное качество кодирования, и со стратегией тестирования (глава 2.4), страховочной сеткой, вообще делающей рефакторинг безопасным. Он также связан с более трудным вопросом технического долга: накопленной стоимости упрощений, стареющих проектов и отложенной уборки, замедляющих каждое будущее изменение. Рефакторинг основной способ погашения этого долга, поэтому обе темы принадлежат одной главе.
В корпоративной и государственной среде ставки растут. Эти системы долгоживущи, часто им десятилетия, и нередко они под аудитом и режимами контроля изменений, воспринимающими любое изменение кода как управляемое событие. Нельзя просто переписать систему пособий для граждан за долгие выходные; вы модернизируете её малыми обратимыми шагами с опорой на свидетельства, и именно это даёт дисциплинированный рефакторинг. Координация этой работы между многими командами и долгоживущими системами (глава 10.4) одна из определяющих трудностей крупномасштабной инженерии, а ошибка в ней то, как организации оказываются замороженными, не в силах менять программы, которые они больше не понимают.
Ключевые принципы
- Рефакторинг сохраняет поведение; если вы меняете то, что делает код, это отдельное изменение, выполняемое отдельно.
- Надёжный набор тестов предпосылка безопасного рефакторинга, а не необязательное дополнение.
- Работайте малыми, названными, обратимыми шагами и держите код рабочим после каждого.
- Делайте технический долг видимым и отслеживаемым, затем финансируйте погашение как устойчивую мощность, а не героизм.
- Рефакторьте код, который вы уже меняете, где уборка оправдывает себя.
- Не весь долг стоит гасить; стабильный, редко затрагиваемый или скоро выводимый код можно оставить в покое.
- Измеряйте внутреннее качество для информирования суждения, никогда как цель для накруток.
Рекомендации
Держите рефакторинг и изменение поведения строго раздельно
Решите до начала, что именно вы делаете, и никогда не смешивайте два в одном коммите. Когда вы рефакторите, тесты, проходившие раньше, должны проходить после, без изменений, потому что наблюдаемое поведение не сдвинулось. Когда вы меняете поведение, делайте это отдельным коммитом со своими тестами. Причина практическая: если смешанное изменение что-то ломает, вы не можете сказать, внесла ли ошибку ваша перестройка или изменение поведения, а на ревью кода (глава 2.5) рецензент не может чисто рассуждать ни об одной из половин. Работает привычка, правило двух шляп Мартина Фаулера: вы всегда носите либо шляпу рефакторинга, либо шляпу функции, знаете, какую именно, и переключаетесь намеренно. Раздельные коммиты также делают историю контроля версий читаемой, так что инженер, проводящий бисекцию сбоя, может с уверенностью пропускать чисто рефакторные коммиты.
Установите надёжную страховочную сетку, прежде чем перестраивать
Рефакторинг без тестов это просто правка в надежде. Прежде чем перестраивать что-либо значимое, вам нужен набор, которому вы доверяете в том, что он поймает изменение поведения, если вы его вызовете, и в этом суть стратегии тестирования (глава 2.4). Для кода, уже имеющего хорошее покрытие, запустите тесты, рефакторьте малыми шагами и запускайте их снова после каждого шага. Для унаследованного кода без тестов честный ход сначала написать характеризующие тесты (characterisation tests). Характеризующий тест не утверждает, что код должен делать; он фиксирует, что код делает прямо сейчас, включая его причуды, так что любое изменение поведения проявляется как падающий тест. Майкл Физерс популяризировал этот подход именно для ситуации, в которой живут большие организации: код, который работает, важен и не имеет тестов. Как только текущее поведение зафиксировано, вы можете безопасно рефакторить под ним и только затем менять поведение поверх.
Учитесь распознавать запахи кода и применять небольшие названные рефакторинги
Запах кода это поверхностный признак того, что под ним может требоваться внимание: функция, ставшая слишком длинной, класс, знающий слишком много, дублированная логика, длинный список параметров, имена, лгущие о том, что делают. Запах это подсказка, а не вердикт, поэтому вы исследуете, а не слепо подчиняетесь. Ответом служит небольшой названный рефакторинг из каталога Фаулера: Extract Function, Rename Variable, Move Method, Replace Conditional with Polymorphism и десятки других. Ценность названных ходов в том, что каждый мал, понятен, механически безопасен и часто напрямую поддерживается вашей IDE. Вы составляете большие улучшения из множества крошечных надёжных шагов, держа код зелёным всё время, а не делаете один большой прыжок, который не можете проверить.
Предпочитайте оппортунистический рефакторинг и резервируйте кампании для настоящей структурной нужды
Большая часть рефакторинга должна быть оппортунистической, вплетённой в уже выполняемую работу. Правило бойскаута передаёт суть: оставьте код немного чище, чем нашли. Когда вы трогаете файл, чтобы добавить функцию или исправить ошибку, вы уже понимаете этот уголок, и небольшие уборки там накапливаются со временем без чьего-либо разрешения и отдельного бюджета. Плановые кампании рефакторинга, когда команда останавливает работу над функциями, чтобы перестроить большую область, иногда необходимы, но они дороги, их трудно планировать при продуктовом давлении, и они рискованны, если область плохо протестирована. Резервируйте кампании для структурных проблем, до которых оппортунистическая уборка не дотягивается, и обосновывайте свидетельствами о стоимости изменений, которую вы платите. Предпочитайте устойчивую капель небольших уборок; она долговечнее редкого героического переписывания.
Используйте шаблон «душащая фига» для крупных структурных изменений
Когда нужно заменить целую подсистему, не пытайтесь переписывать «большим взрывом», тянущимся год и сливающимся в конце; так умирают проекты модернизации. Используйте шаблон «душащая фига» (strangler fig), названный Мартином Фаулером по лиане, обвивающей дерево и постепенно его заменяющей. Вы ставите фасад перед старой системой, направляете один срез функциональности за раз на новый код за этим фасадом, проверяете в эксплуатации и повторяете, пока старая система не будет полностью окружена и её можно будет убрать. Каждый срез мал, пригоден к выпуску и обратим, так что риск остаётся ограниченным, а ценность приходит непрерывно. Близкий родственник, ветвление через абстракцию, делает то же внутри одной кодовой базы: вы вводите слой абстракции над заменяемым, строите новую реализацию за ним, пока обе сосуществуют, постепенно переключаете потребителей и удаляете старую реализацию, как только ничто от неё не зависит. Оба позволяют унаследованной системе эволюционировать, оставаясь живой, а это единственный вид модернизации, который большинство больших организаций действительно могут себе позволить.
Воспринимайте технический долг как портфель и делайте его видимым
Метафора долга, введённая Вардом Каннингемом, разделяет две вещи: основной долг (сам грязный код или упрощение) и проценты (дополнительные усилия, которые платит каждое будущее изменение из-за него). Не весь долг равен. Квадрант Фаулера сортирует его по двум осям: намеренный против непреднамеренного и благоразумный против безрассудного. Благоразумно-намеренный долг («мы выпускаем сейчас и приберёмся в следующем спринте, и мы знаем цену») законное бизнес-решение. Безрассудно-непреднамеренный долг («а что такое паттерн проектирования?») просто ущерб. Задача управления, связанная с принятием решений и управлением (глава 1.5) и её рассмотрением долга как портфеля, в том, чтобы сделать долг видимым и рассуждать о нём: отслеживать значимые позиции там, где живёт работа, помечать код и записывать платимые проценты, чтобы погашение конкурировало за мощность на основании свидетельств, а не того, кто громче жалуется. Долг, который вы не видите, вы не можете им управлять.
Финансируйте погашение как устойчивую мощность, а не героизм
Режим отказа это воспринимание уборки как дела, которое вы сделаете, «когда всё утихнет», то есть никогда. Долговечный шаблон это фиксированная защищённая мощность на погашение: явная доля каждого цикла или постоянная договорённость, что уборка идёт вместе с работой над функциями в той же области. Не работает периодический героический спринт, когда кто-то сжигает выходные, чтобы всё исправить, потому что он неустойчив, нерецензируем и обычно сам себя отменяет. Устойчивая мощность держит платежи по процентам низкими и избегает цикла подъёма и спада, где долг нарастает, пока кризис не вынудит дорогое переписывание. Это управленческое обязательство не меньше, чем инженерная практика, и оно принадлежит тому, как вы планируете сопровождение программного обеспечения (глава 3.7) на протяжении жизни системы.
Измеряйте внутреннее качество, но не позволяйте мере стать целью
Внутреннее качество можно измерять сигналами вроде цикломатической сложности (числа независимых путей через функцию), дублирования, покрытия тестами, доли неудачных изменений и времени, которое занимают изменения в областях, вызывающих подозрение. Эти числа полезны, чтобы выявлять, где сосредоточен долг, и наблюдать тренд во времени. Опасность в законе Гудхарта: когда мера становится целью, она перестаёт измерять что-либо реальное. Предпишите число покрытия, и получите тесты без утверждений; вознаграждайте низкие оценки сложности, и получите логику, размазанную по большему числу функций ради обхода метрики. Используйте метрики, чтобы начинать разговоры и находить горячие точки, и никогда не привязывайте метрику качества к шлюзу, который люди заинтересованы накручивать.
Знайте, когда не рефакторить
Рефакторинг это вложение, и некоторый код никогда его не окупит. Если модуль стабилен, редко затрагивается и понимается достаточно хорошо, чтобы менять его в те редкие случаи, когда нужно, его уборка это усилия, потраченные ради процентов, которые вы не платили. Если код намечен к выводу, его рефакторинг полировка того, что вы собираетесь выбросить. Дисциплина в том, чтобы тратить бюджет на уборку там, где изменения часты и болезненны, потому что именно там снижение процентов накапливается, и оставлять тихие уголки в покое.
Компромиссы: плюсы и минусы
| Подход | Плюсы | Минусы |
|---|---|---|
| Оппортунистический рефакторинг (правило бойскаута) | Дёшево, непрерывно, не нужен отдельный бюджет, накапливается со временем | Неравномерный охват; горячие файлы улучшаются, пока холодные гниют |
| Плановая кампания рефакторинга | Исправляет структурные проблемы, до которых уборка не дотягивается | Дорого; конкурирует с функциями; рискованно без хороших тестов |
| Душащая фига / ветвление через абстракцию | Постепенно, обратимо, держит систему живой, ограничивает риск | На бумаге медленнее переписывания; нужна дисциплина довести до конца |
| Переписывание «большим взрывом» | Чистый лист; нет ограничений наследия | Высокая доля провалов; долгое время до ценности; пробелы в поведении |
| Намеренный благоразумный долг | Выпускает ценность сейчас; явное плановое погашение | Становится безрассудным, если погашение так и не запланировано |
| Качество под шлюзом метрик | Объективно, видимо, рано ловит дрейф | Приглашает накрутки; наказывает нюансы; может ухудшать реальное качество |
Основное напряжение это скорость сейчас против изменяемости потом, и оно реально. Выпуск упрощения может быть верным решением, когда срок подлинный, а долг благоразумный и отслеживаемый. Ошибка в том, чтобы делать вид, что долг бесплатен, или позволять ему накапливаться невидимо, пока систему не станет слишком дорого менять. Разрешайте это, делая размен явным каждый раз: назовите долг, оцените проценты, решите намеренно и запишите решение, чтобы погашение можно было запланировать, а не забыть. Команда, берущая в долг сознательно и платящая устойчиво, остаётся быстрой годами; команда, берущая в долг вслепую, останавливается.
Вопросы для обсуждения с командой
Как мы не даём рефакторингу и изменению поведения затекать в один коммит и действительно ли наше ревью это обеспечивает? Это основополагающая дисциплина всей главы и та, что чаще всего нарушается под давлением срока, потому что кажется эффективным «прибраться здесь, раз я уж тут» и выпустить всё вместе. Стоимость приходит позже: когда смешанный коммит ломает эксплуатацию, никто не может сказать, вызвала ли это перестройка или функция, а бисекция по вашей истории перестаёт быть надёжной. Принесите горстку недавних запросов на слияние и честно проверьте, сколько смешивали две шляпы. Противоположное соображение это трение, поскольку разбиение работы на отдельные коммиты чуть больше усилий заранее. Ответ должен определить ваши соглашения о коммитах и контрольный список ревью.
Где наш технический долг, сколько процентов мы по нему платим и кто решает, что гасить? Большинство команд не могут ответить на это, и в этом настоящая проблема, потому что долгом, которого не видно, управляет тот, кто громче жалуется, а не там, где реально стоимость. Сделать его видимым означает отслеживать значимые позиции, помечать код и собирать свидетельства о том, какие области делают изменения медленными и склонными к сбоям. Противоположное тяготение в том, что каждый час, потраченный на погашение, это час, не потраченный на функции, поэтому решение должно быть портфельным решением, принимаемым вместе с руководством и связанным с тем, как вы управляете инженерной работой (глава 1.5). Принесите данные о доле неудачных изменений и список файлов, которых все боятся касаться. Ответ должен превратиться в защищённую устойчивую мощность на погашение, а не расплывчатое намерение прибраться, когда всё утихнет.
Какие части нашей кодовой базы нам следует намеренно не рефакторить и как мы это узнаем? Рефакторить всё столь же провал, как не рефакторить ничего, потому что усилия, потраченные на уборку стабильного, редко затрагиваемого или скоро выводимого кода, это проценты по займу, которого вы не должны. Суждение реально: модуль может выглядеть уродливо и всё равно быть неверным местом для вложений, если его никто не меняет. Принесите данные о частоте изменений вместе с сигналами сложности, потому что пересечение высокой изменчивости и высокой сложности то место, где уборка накапливается, тогда как редко меняемый код обычно лучше оставить. Противоположный риск в том, что «мы оставим это» становится предлогом никогда не трогать ничего трудного. Ответ должен дать явный короткий список горячих точек, достойных вложений, и разрешение игнорировать тихие уголки.
Достаточно ли мы доверяем нашему набору тестов, чтобы рефакторить код, который нам больше всего нужно менять, и где нам пришлось бы сначала писать характеризующие тесты? Страховочная сетка, которой нельзя доверять, превращает рефакторинг в правку в надежде, а в большой команде самый страшный код обычно меньше всего протестирован, то есть именно там, где уборка окупилась бы сильнее всего. Принесите данные о покрытии и доле неудачных изменений для ваших горячих точек и будьте честны, какие критические модули не дали бы вам предупреждения, если бы перестройка изменила поведение. Противоположное соображение в том, что написание характеризующих тестов для унаследованного кода медленная неброская работа, не выпускающая функций, поэтому её легко откладывать вечно. В корпоративных и государственных системах под аудитом и контролем изменений эти зафиксированные тесты ещё и свидетельство того, что изменение сохранило поведение, поэтому их финансирование мера безопасности и соответствия одновременно; ответ должен назвать, какие области получают тестовую оснастку, прежде чем кто-либо их тронет.
Когда подсистему действительно нужно заменить, как мы решаем между постепенным подходом «душащей фиги» и переписыванием и у кого есть власть сказать «нет» переписыванию? Переписывание «большим взрывом» самый соблазнительный и наиболее склонный к провалу вариант на столе, потому что чистый лист на бумаге всегда выглядит дешевле жизни со старыми ограничениями. Для большой организации постепенный путь (фасад, один срез за раз, проверка в эксплуатации) держит систему живой и ограничивает риск, но он медленнее, требует дисциплины довести до конца и конкурирует с аппетитом к новому началу. Принесите карту частоты изменений подсистемы, честную оценку того, как долго переписывание шло бы до ценности, и пробелы в поведении, которые параллельное переписывание должно было бы закрыть. В государственной и регулируемой среде многолетнее переписывание, сливающееся в конце, редко выживает под аудитом, поэтому ответ должен по умолчанию выбирать «душащую фигу» или ветвление через абстракцию и воспринимать любое переписывание как исключение, которое нужно отстаивать свидетельствами.
Как использовать метрики внутреннего качества, чтобы находить, где концентрируется долг, не давая числу стать целью, которую люди накручивают? Метрики вроде сложности, дублирования, покрытия и доли неудачных изменений единственный способ для большой организации видеть код, который не читает ни один человек, но в момент, когда одна привязана к шлюзу или ревью результативности, действует закон Гудхарта, и число перестаёт измерять что-либо реальное. Принесите примеры того, где метрика уже движет поведением, и спросите, начинает ли она разговоры или тихо вознаграждает тесты без утверждений и логику, размазанную по функциям ради обхода порога. Противоположное тяготение в том, что руководство хочет простое число на панели, а «используйте суждение» продать труднее, чем зелёную полосу. В корпоративной и государственной среде, где метрики питают управленческую отчётность, будьте явны, что сигналы качества информируют вложения и находят горячие точки, но никогда не служат шлюзом для людей; ответ должен провести твёрдую черту между измерением ради обучения и измерением ради суждения.
Взгляд по секторам
Стартап. С горсткой инженеров и без запаса времени рефакторьте только оппортунистически: носите одну шляпу на коммит, чтобы история оставалась пригодной для бисекции, и ведите короткий честный список упрощений, на которые пошли намеренно. Не запускайте кампании уборки и не полируйте стабильные модули; тратьте скудное внимание на один файл, которого все боятся, и пишите характеризующие тесты только там, где изменение действительно пугает. Намеренный, видимый долг на этой стадии нормален; безрассудный невидимый долг то, что вас убивает.
Малый бизнес. Без выделенного специалиста по платформе или инструментам и при ограниченном бюджете опирайтесь на то, что IDE и экосистема языка дают бесплатно: автоматические переименования и выделения, линтер и базовый сигнал покрытия. Воспринимайте большую часть долга как то, чем вы управляете в ходе обычной работы, а не то, что нанимаете консультанта исправлять, и предпочитайте покупку хорошо сопровождаемых библиотек их построению с последующим рефакторингом. Резервируйте редкие платные усилия для единственной системы, чья медлительность прямо стоит вам клиентов.
Крупная организация. Между многими командами проблема в портфельном управлении: общий реестр долга, единообразная разметка горячих точек по частоте изменений и сложности и защищённая доля мощности каждой команды на погашение, чтобы уборка перестала по умолчанию проигрывать функциям. Стандартизируйте дисциплину двух шляп и практику характеризующих тестов, чтобы любой инженер, переходящий между командами, находил те же правила, и используйте «душащую фигу» и ветвление через абстракцию для структурных изменений, согласуемых между группами. Держите метрики качества информационными, чтобы они находили долг, не накручиваясь в ревью результативности.
Государственный сектор. Долгоживущие системы под строгим аудитом и контролем изменений делают дисциплинированный рефакторинг активом соответствия, а не только инженерным: строгое отделение перестройки от изменения поведения позволяет аудиторам видеть, какие именно коммиты изменили поведение, а какие лишь прибрались. Правила закупок и прозрачности отдают предпочтение малым обратимым шагам со свидетельствами, а не переписыванию «большим взрывом», поэтому по умолчанию выбирайте «душащую фигу» с характеризующими тестами, документирующими сохранение поведения. Сделайте реестр долга и план его погашения частью записи сопровождения системы, чтобы надзорные органы получали требуемую прослеживаемость.
Примеры
Стартап. Стартап из шести человек выпускает быстро и знает, что берёт долг, поэтому хорошо делает две дешёвые вещи. Каждый запрос на слияние носит одну шляпу: рефакторные коммиты отделены от функциональных, что сохраняет историю пригодной для бисекции даже на высокой скорости. И они ведут короткий честный список упрощений, на которые пошли намеренно, с однострочной пометкой о стоимости процентов по каждому. Когда платёжный модуль становится файлом, которого все боятся, этот список плюс история их неудачных изменений обосновывают две затраченные на выделение более чистой границы дня. Они пишут характеризующие тесты, фиксирующие текущее поведение, рефакторят под ними переименованиями и выделениями IDE и никогда не трогают стабильные модули, которые никто не меняет. Долг, который они несут, намеренный и видимый, поэтому никогда не становится безрассудным.
Крупная организация. Международная логистическая компания ведёт пятнадцатилетнюю систему заказов, которую многие команды меняют каждую неделю. Вместо переписывания они принимают шаблон «душащая фига»: фасад стоит перед монолитом, и по одной ограниченной возможности за раз перенаправляется на новые сервисы за ним, проверяясь в эксплуатации до начала следующего среза. Координация этого между командами и долгоживущей системой (глава 10.4) самая трудная часть, поэтому они ведут общий реестр долга, помечают горячие точки по частоте изменений и сложности и резервируют фиксированную долю мощности каждой команды на погашение. Метрики внутреннего качества подсказывают, куда смотреть, но никогда не служат шлюзом для чьего-либо ревью результативности, что сохраняет честность чисел. За два года монолит неуклонно сжимается, и ни одно изменение никогда не рискует всей системой.
Государственный сектор. Национальное налоговое ведомство должно модернизировать десятилетнюю платформу оценки под строгим аудитом и правилами контроля изменений, где каждое изменение кода управляемое событие со свидетельствами. Переписывание «большим взрывом» невозможно, поэтому они используют ветвление через абстракцию: над унаследованным расчётным движком вводится слой абстракции, за ним строится новая реализация, а потребители переносятся по одному налоговому правилу за раз, каждый перенос документируется как небольшое обратимое изменение с характеризующими тестами, доказывающими неизменность поведения. Поскольку рефакторинг строго отделён от любого изменения поведения по законодательству, аудиторы видят, какие именно коммиты изменили поведение, а какие лишь перестроили. Реестр долга и план его погашения становятся частью записи сопровождения системы (глава 3.7), давая надзорным органам требуемую прослеживаемость.
Бизнес-обоснование: мотивы, ROI и TCO
Отдача от рефакторинга и погашения долга это устойчивая способность дёшево менять программы, а для большинства систем основная часть стоимости жизненного цикла приходится на сопровождение, поэтому именно здесь во многом решается совокупная стоимость владения. Проценты по техническому долгу платятся в валюте, которую руководство уже отслеживает: более медленная поставка, более высокая доля неудачных изменений, более долгое время восстановления после инцидентов и инженеры, избегающие самого страшного кода. Когда вы делаете долг видимым и финансируете погашение устойчиво, вы снижаете стоимость каждого будущего изменения в самых важных областях и избегаете цикла подъёма и спада, где пренебрежённый долг вынуждает дорогое аварийное переписывание.
Стоимость внедрения скромна и в основном культурна: ввести дисциплину двух шляп, построить страховочную сетку там, где нужно рефакторить, вести реестр долга и защитить устойчивую долю мощности на погашение. Стоимость пренебрежения нарастает молча. Проценты накапливаются на каждом изменении, пока скорость не рухнет, а организация не окажется замороженной, неспособной безопасно менять систему, которую она больше не понимает, а это самый дорогой исход из всех. Чтобы обосновать руководству, свяжите долг напрямую с метриками поставки, которые его интересуют, и подавайте погашение как портфельное решение с измеримой отдачей, а не как просьбу инженеров дать время прибраться.
Антипаттерны и ловушки
- Смешивание рефакторинга с изменением поведения: один коммит делает и то и другое, поэтому поломку нельзя атрибутировать, а история становится ненадёжной.
- Рефакторинг без страховочной сетки: перестройка нетестированного кода в надежде, то есть правка на вере.
- Переписывание «большим взрывом»: замена работающей системы целиком сразу, шаблон с высокой долей провалов и долгим временем до ценности.
- Рефакторинг как героические выходные: нерецензируемая неустойчивая уборка, сама себя отменяющая вместо устойчивой мощности.
- Невидимый долг: упрощения, которые никто не отслеживает, поэтому погашение определяется объёмом жалоб, а не реальной стоимостью.
- Накрутка метрик качества: достижение цели по покрытию или сложности при падающем реальном качестве, потому что мера стала целью.
- Рефакторинг не того кода: полировка стабильных или скоро выводимых модулей, пока настоящие горячие точки продолжают стоить вам.
- Вечный рефакторинг: бесконечная перестройка, никогда не выпускающая ценность, зеркальное отражение того, чтобы никогда не прибираться.
Модель зрелости
- Уровень 1, Начальный: Рефакторинг стихиен и реактивен, часто смешан с изменением поведения в одном коммите. Нет надёжной страховочной сетки, технический долг невидим и не отслеживается, а уборка происходит лишь в редких героических рывках или вообще нет.
- Уровень 2, Развитие: Некоторые команды отделяют рефакторинг от изменения поведения и опираются на тесты там, где они есть, а названные рефакторинги и характеризующие тесты появляются в карманах. Практика непоследовательна между командами, долг обсуждается и иногда записывается, а погашение стихийно конкурирует с функциями и обычно проигрывает.
- Уровень 3, Стандартизация: Дисциплина двух шляп, характеризующие тесты для унаследованного кода и небольшие названные рефакторинги задокументированы и ожидаются по всей организации. Долг отслеживается в общем реестре, разделяющем основной долг и проценты, а защищённая мощность на погашение планируется каждый цикл и обеспечивается на ревью.
- Уровень 4, Управление: Долг и уборка измеряются и контролируются данными относительно базовых значений. Вы отслеживаете частоту изменений и сложность для выявления горячих точек, наблюдаете долю неудачных изменений и время выполнения изменений в рефакторенных областях и записываете проценты, которые стоит каждая значимая позиция, так что решения о погашении опираются на свидетельства, а решения «убрать или вложиться» принимаются по трендам, а не объёму жалоб. Сигналы качества информируют вложения, не привязываясь к шлюзам, которые люди могут накручивать.
- Уровень 5, Оркестрация: Долгом управляют как непрерывно перебалансируемым портфелем, интегрированным с продуктовым планированием и планированием сопровождения по всей организации. Структурные изменения регулярно используют «душащую фигу» и ветвление через абстракцию, согласуемые между командами, погашение непрерывно и согласовано с тем, где изменения часты и болезненны, а практика адаптируется по мере смещения системы и картины рисков, так что долгоживущий код остаётся изменяемым десятилетиями.
Идеи для обсуждения
- Каково реальное обеспечиваемое правило вашей команды о разделении рефакторинга и изменения поведения и где оно ломается под давлением срока?
- Как вы решаете на основании свидетельств, какой код заслуживает уборки, а какой лучше оставить?
- Где характеризующие тесты позволили бы вам безопасно рефакторить унаследованную область, которой вы сейчас избегаете?
- Как выглядел бы подход «душащая фига» для вашей следующей крупной модернизации и какой фасад или абстракцию вы ввели бы первой?
- Кто владеет реестром технического долга и как погашение на самом деле выигрывает мощность у работы над функциями?
Главные выводы
- Рефакторинг сохраняет поведение; держите его строго отдельно от изменения поведения, в отдельных коммитах.
- Надёжный набор тестов предпосылка безопасного рефакторинга, а характеризующие тесты дают его унаследованному коду.
- Работайте малыми, названными, обратимыми шагами, предпочитайте оппортунистическую уборку и используйте «душащую фигу» или ветвление через абстракцию для крупных структурных изменений.
- Делайте технический долг видимым, разделяйте основной долг и проценты и финансируйте погашение как устойчивую мощность, а не героизм.
- Измеряйте внутреннее качество для направления суждения, никогда как цель для накруток, и не рефакторьте код, который стабилен или намечен к выводу.
Источники и дополнительное чтение
- Martin Fowler, Refactoring: Improving the Design of Existing Code, second edition
- Michael Feathers, Working Effectively with Legacy Code
- Ward Cunningham, The WyCash Portfolio Management System (OOPSLA 1992 experience report, origin of the debt metaphor)
- Martin Fowler, “TechnicalDebtQuadrant” and “StranglerFigApplication” (martinfowler.com)
- Kent Beck, Tidy First? A Personal Exercise in Empirical Software Design
- Steve McConnell, Code Complete: A Practical Handbook of Software Construction
- Robert C. Martin, Clean Code: A Handbook of Agile Software Craftsmanship