Масштабируемый рефакторинг. Возвращаем контроль над кодом - Мод Лемер - E-Book

Масштабируемый рефакторинг. Возвращаем контроль над кодом E-Book

Мод Лемер

0,0

Beschreibung

Поддерживать большие приложения сложно, а поддержка больших «неорганизованных» приложений превращается в непосильную задачу. Пришло время сделать паузу и задуматься о рефакторинге! Внесение значительных изменений в крупную и сложную кодовую базу — нетривиальная задача, которую практически невозможно успешно выполнить без рабочей команды, инструментов и планирования. Мод Лемер раскрывает все тайны рефакторинга на примере двух исследований. Вы научитесь эффективно вносить важные изменения в кодовую базу, узнаете, как деградирует код и почему иногда это неизбежно.

Sie lesen das E-Book in den Legimi-Apps auf:

Android
iOS
von Legimi
zertifizierten E-Readern
Kindle™-E-Readern
(für ausgewählte Pakete)

Seitenzahl: 305

Veröffentlichungsjahr: 2023

Das E-Book (TTS) können Sie hören im Abo „Legimi Premium” in Legimi-Apps auf:

Android
iOS
Bewertungen
0,0
0
0
0
0
0
Mehr Informationen
Mehr Informationen
Legimi prüft nicht, ob Rezensionen von Nutzern stammen, die den betreffenden Titel tatsächlich gekauft oder gelesen/gehört haben. Wir entfernen aber gefälschte Rezensionen.



Мод Лемер
Масштабируемый рефакторинг. Возвращаем контроль над кодом

Переводчик И. Рузмайкина

Литературный редактор Т. Сажина

Художник В. Мостипан

Корректоры С. Беляева, Г. Шкатова

Мод Лемер

Масштабируемый рефакторинг. Возвращаем контроль над кодом. — СПб.: Питер, 2022.

ISBN 978-5-4461-3921-7

© ООО Издательство "Питер", 2022

Все права защищены. Никакая часть данной книги не может быть воспроизведена в какой бы то ни было форме без письменного разрешения владельцев авторских прав.

Оглавление

Предисловие
Для кого предназначена книга
Почему я решила написать эту книгу
Структура издания
Условные обозначения
Примеры кода
Благодарности
От издательства
Часть I. Введение
Глава 1. Рефакторинг
Что такое рефакторинг
Что такое масштабируемый рефакторинг
Зачем нужен рефакторинг
Выгоды рефакторинга
Риски рефакторинга
Когда начинать рефакторинг
Когда не нужно делать рефакторинг
Первый пример рефакторинга
Глава 2. Как деградирует код
Почему важно понимать, что код деградирует
Изменение требований
Технический долг
Применение знаний
Часть II. Планирование
Глава 3. Количественная характеристика начального состояния
Почему сложно оценить последствия рефакторинга
Оценка сложности кода
Метрики покрытия кода
Документация
Управление версиями
Репутация
Составляем полную картину
Глава 4. Составление плана
Определение конечного состояния
Поиск кратчайшего расстояния
Промежуточные шаги
Выбор стратегии развертывания
Очистка кода
Ссылка на метрики
Оценка
Обсуждение планов с другими командами
Уточненный план
Глава 5. Получение одобрения
Причины несогласия руководителей
Поиск убедительной аргументации
Заинтересованность в рефакторинге
Глава 6. Подбор команды
Выбор экспертов
Подбор специалистов
Типы команд, выполняющих рефакторинг
Предложение
Возможные результаты
Создание сильных команд
Часть III. Выполнение
Глава 7. Коммуникация
Внутри команды
Вне команды
Экспериментируйте
Глава 8. Стратегии выполнения
Формирование команды
Учет результатов
Продуктивное программирование
Заключение
Глава 9. Закрепление результатов рефакторинга
Содействие принятию рефакторинга
Образование
Закрепление
Интеграция улучшений в культуру
Часть IV. Разборы примеров
Глава 10. Избыточные схемы базы данных
Slack 101
Архитектура Slack 101
Проблемы масштабирования
Консолидация таблиц
Заключительные шаги
Извлеченные уроки
Анализ истории кода
Ключевые моменты
Глава 11. Переход к новой базе данных
Распределение по рабочим пространствам
Миграция таблицы channels_members на Vitess
Разбиение запросов с оператором JOIN
Сложности развертывания
Заключительные шаги
Извлеченные уроки
Основные моменты
Об авторе
Об обложке
Рекомендуем прочитать

Предисловие

Рефакторингу посвящено уже много книг, но в большинстве из них рассматриваются детали улучшения небольших частей кода по строке за раз. Однако мне кажется, что самая сложная часть рефакторинга не в поиске точного способа улучшения кода, а скорее в происходящих вокруг него процессах. Можно пойти еще дальше и сказать, что для любого крупного программного проекта мелочи редко имеют значение. Самая большая проблема кроется в координации сложных изменений.

В книге я попытаюсь помочь вам разобраться в этих сложностях. Она представляет собой результат многолетнего опыта выполнения всевозможных вариантов рефакторинга. Благодаря многим проектам, которыми я руководила в Slack, компания смогла сильно расшириться. Изначально у нашего крупнейшего клиента было 25 000 сотрудников. Теперь мы поддерживаем приложение, где общается организация, насчитывающая 500 000 человек. Разработанные нами стратегии для эффективного рефакторинга позволяют приспособиться к взрывному росту организации. Наша собственная команда инженеров выросла почти в шесть раз. Успешно запланировать и выполнить проект, затрагивающий как значительную часть кодовой базы, так и сотрудников, очень непросто. Я надеюсь, что эта книга даст вам необходимые инструменты и знания.

Для кого предназначена книга

Если вы вместе с десятками других коллег работаете с большой сложной кодовой базой, эта книга для вас!

Если вы молодой разработчик, желающий улучшить свои навыки и изменить к лучшему ситуацию в компании, участие в серьезном рефакторинге даст вам такую возможность. Последствия подобных проектов выходят далеко за рамки отдельной команды. При этом они не настолько интересны, чтобы за них сразу же хватались старшие инженеры. Но для молодых сотрудников участие в рефакторинге — отличная возможность приобрести новые профессиональные навыки (и укрепить уже имеющиеся). Эта книга расскажет, как органично выполнять такой проект от начала до конца.

Информация отсюда станет полезным ресурсом и для опытных специалистов, способных самостоятельно решить любую проблему, но разочаровавшихся из-за того, что другие не понимают ценности их работы. Тем, кто ищет способы подтянуть коллег до своего уровня, эта книга расскажет о стратегиях, с помощью которых можно стимулировать их посмотреть на технические проблемы с вашей точки зрения.

Техническим директорам, стремящимся помочь своим подчиненным в выполнении крупномасштабного рефакторинга, эта книга покажет, как поддерживать свою команду на каждом этапе проекта. Я предпочла не углубляться в технические детали, поэтому мои рекомендации будут полезны всем участникам рефакторинга независимо от специализации.

Почему я решила написать эту книгу

Участвуя в первом рефакторинге, я понимала, зачем менять код, и знала, как это выполнить. Но не имела представления о том, как сделать это безопасно, постепенно, не вызывая всеобщего недовольства. Мне хотелось, чтобы изменения затронули как можно больше сфер. Я не задумывалась о таких вещах, как влияние рефакторинга на работу других отделов или способы привлечения коллег к участию в проекте. Я просто шла вперед (все детали описаны в главе 10).

В последующие годы мне пришлось участвовать в переделке множества строк кода, в результате чего мы иногда получали плохо выполненные проекты. Но я извлекла из тех случаев несколько важных уроков, о которых стала рассказывать на конференциях. Эти выступления нашли отклик у сотен разработчиков, которые тоже сталкивались с проблемами рефакторинга больших фрагментов кода. Было очевидно, что в наших знаниях по этой теме есть пробелы. Особенно это касалось профессионального подхода к написанию программного обеспечения. Во многих смыслах эта книга пытается учить важным вещам, не входящим в стандартную учебную программу по информатике. Преподавать все это в классе слишком сложно. Возможно, по книге научить этому тоже не очень реально, но почему бы не попробовать.

Структура издания

Книга разделена на четыре части и разбита на главы в соответствии с хронологическим порядком этапов выполнения крупномасштабного рефакторинга.

• Часть I знакомит с важными концепциями рефакторинга.

• В главе 1 обсуждаются основы и отличие масштабного рефакторинга от более мелких переделок кода.

• Глава 2 описывает причины деградации кода и ее влияние на эффективность рефакторинга.

• Часть II посвящена всему, что нужно знать о планировании рефакторинга.

• В главе 3 представлен обзор метрик для количественной оценки проблем, которые пытается решить рефакторинг. Вы узнаете, как в цифрах описать исходное состояние системы.

• Глава 4 объясняет, из каких компонентов состоит план выполнения рефакторинга и как его правильно составить.

• В главе 5 обсуждаются способы получения поддержки рефакторинга от руководства всех уровней и коллег.

• Глава 6 описывает, как определить лучших кандидатов для работы над рефакторингом, и дает советы, как привлечь их к участию в проекте.

• В части III вы узнаете, как убедиться в корректном протекании рефакторинга на каждом этапе его выполнения.

• Глава 7 рассказывает, как общаться и взаимодействовать с членами команды и любыми внешними заинтересованными сторонами.

• В главе 8 рассматриваются способы сохранения темпов выполнения рефакторинга.

• Глава 9 советует, как лучше сохранить изменения после рефакторинга.

• В части IV рассматриваются примеры из проектов с моим участием в компании Slack. Эти проекты сильно повлияли на работу значительной части нашего основного приложения. Я надеюсь, что это поможет проиллюстрировать концепции из частей I–III.

Главы необязательно читать по порядку. Переход к новому этапу рефакторинга не означает, что мы не будем пересматривать сделанное ранее. Например, можно приступить к рефакторингу, имея четкое представление о команде, которая будет над ним работать, и, уже пройдя половину пути, обнаружить, что на самом деле нужно привлечь к проекту больше разработчиков, чем планировалось. Ничего страшного. Такие ситуации естественны!

Условные обозначения

В книге используются следующие условные обозначения:

Курсив

Курсивом выделены новые термины.

Моноширинныйшрифт

Используется для листингов программ и внутри абзацев для обозначения таких элементов, как переменные и функции, базы данных, типы данных, переменные среды, операторы и ключевые слова, имена файлов и их расширений.

Шрифт без засечек

Используется для обозначения URL, адресов электронной почты, названий кнопок, каталогов.

Этот рисунок указывает на совет или предложение.

Этот рисунок указывает на примечание.

Примеры кода

Дополнительные материалы (примеры кода, упражнения и прочее) доступны по адресу https://github.com/qcmaude/refactoring-at-scale.

Эта книга предназначена помочь вам в работе. Поэтому все приведенные в тексте примеры кода вы можете использовать в своих программах и документации. Вам не нужно связываться с нами для получения разрешения, если вы не воспроизводите значительную часть кода. Если в вашей программе есть несколько фрагментов кода из этой книги, разрешения на использование не требуется, как и для ответов на вопросы с цитированием примеров кода. А вот для продажи или распространения примеров из книг издательства O’Reilly получить разрешение понадобится, как и на включение большого количества примеров кода в документацию по вашему продукту.

Мы ценим указание авторства, хотя и не требуем его. Атрибуция обычно включает название, автора, издателя и ISBN. Например: «Масштабируемый рефакторинг», Мод Лемер («Питер»). Copyright 2021 Maude Lemaire, 978-5-4461-3921-7».

Если вы не уверены в том, что ваш вариант использования примеров кода не выходит за описанные выше рамки, не стесняйтесь обращаться за уточнениями по адресу [email protected].

Благодарности

Писать книгу непросто, и эта работа не стала исключением. Издание не появилось бы на свет без вклада множества людей.

Прежде всего я хочу поблагодарить своего редактора Джеффа Блейла. Именно он превратил меня из неопытного писателя в автора книги. Его отзывы всегда были точны и помогали мне более связно излагать мои мысли. Я просто не могу представить лучшего редактора для своей книги.

Хочу поблагодарить друзей и коллег, которые читали ранние версии нескольких глав: Моргана Джонса, Райана Гринберга и Джейсона Лишку. Их отзывы убедили меня в правильности моих идей и ценности информации для читателей. За поддержку и беседы, заставляющие задуматься, спасибо Джоанн, Кевину, Чейзу и Бену.

Я хотела бы поблагодарить Мэгги Чжоу за помощь в написании второго примера рефакторинга (глава 11). Она одна из самых вдумчивых, умных и энергичных коллег, с которыми мне когда-либо приходилось работать. Я очень рада, что мир сможет узнать о наших совместных приключениях!

Огромное спасибо моим научным редакторам Дэвиду Коттреллу и Генри Робинсону. Дэвид — мой близкий друг со времен учебы в университете. За годы работы в Google он руководил рядом крупномасштабных рефакторингов, а потом основал собственную компанию. Генри — мой коллега в Slack. Именно он множество раз вносил свой вклад в разработку программного обеспечения с открытым исходным кодом и лично видел стремительный рост компаний Кремниевой долины. Они оба невероятно добросовестные разработчики, чье руководство и мудрость очень пригодились мне в работе над книгой. Я бесконечно благодарна им за часы, потраченные на проверку текста. Любые неточности в окончательной рукописи — только мои собственные ошибки.

Спасибо всем, кто когда-либо участвовал со мной в рефакторинге. Вас слишком много, чтобы перечислять поименно, но вы знаете, кого я имею в виду. Вы все помогли в формировании идей, изложенных мной в книге.

Спасибо за поддержку моей семье (Саймону, Мари-Жозе, Франсуа-Реми, Софи, Сильвии, Джерри, Стефани и Селии).

Наконец, спасибо моему мужу Эйвери. За терпение и за то, что давал мне время, место и поддержку в процессе работы. Спасибо, что позволял мне днями обсуждать возникающие идеи. Спасибо, что верил в меня. Эта книга настолько же твоя, как и моя. Я люблю тебя.

От издательства

Ваши замечания, предложения, вопросы отправляйте по адресу [email protected] (издательство «Питер», компьютерная редакция).

Мы будем рады узнать ваше мнение!

На веб-сайте издательства www.piter.com вы найдете подробную информацию о наших книгах.

Часть I. Введение

Глава 1. Рефакторинг

Однажды меня спросили, чем мне так нравится рефакторинг. Почему на работе я то и дело возвращаюсь к подобным проектам? Я ответила, что такие вещи затягивают. Может, это просто любовь к наведению порядка. Все равно что аккуратно рассортировать специи в кухонном шкафу. Или радость избавления от беспорядка. Как возможность отнести мешок со старой одеждой в секонд-хенд. Или даже голос в голове, напоминающий, что крошечные постепенные изменения улучшат рабочие будни моих коллег. Но, скорее всего, все сразу.

В рефакторинге есть нечто, что нравится всем. Это может быть работа над новым функционалом или над масштабированием инфраструктуры. Всегда нужно искать баланс между написанием большего или меньшего количества кода. Важно думать о том, к чему приведут намеренно или случайно внесенные изменения. Код — это живой организм. Когда я думаю, что написанный мной код просуществует еще пять или десять лет, то невольно вздрагиваю. Надеюсь, что к тому времени кто-то либо полностью удалит его, либо заменит более приличной версией, подходящей для актуальных нужд приложения. Это и есть суть рефакторинга.

В этой главе мы определим несколько концепций. Начнем с базового определения рефакторинга. Взяв его за основу, определим масштабируемый рефакторинг. Чтобы развить некоторые положения из этой книги, я расскажу, почему важно серьезно относиться к рефакторингу и какие преимущества дает отработка этого навыка. Мы поговорим о том, чего можно ожидать от рефакторинга и какие риски следует учитывать, принимая решение о его необходимости. Мы составим баланс преимуществ и недостатков рефакторинга. На его основе рассмотрим сценарии, где к нему можно прибегать и где от него лучше воздержаться. Завершит главу небольшой пример практического применения этих концепций.

Что такое рефакторинг

Простыми словами, рефакторинг — это процесс, с помощью которого мы реструктурируем код (factoring) без изменения его внешнего поведения. Если вы думаете, что это очень общее и размытое определение, не волнуйтесь. Я умышленно даю его в таком виде! Рефакторинг может принимать разные формы в зависимости от кода. Для примера определим «систему» как код, принимающий набор выходных данных и на выходе дающий какие-то другие данные.

На рис. 1.1 показана конкретная реализация системы S. Ее создавали в сжатые сроки, так что на качестве авторы сэкономили. Со временем образовалась груда запутанного кода. К счастью, пользователей системы этот внутренний беспорядок никак не затрагивает. Они взаимодействуют с S через интерфейс и получают приемлемые результаты.

Рис. 1.1. Простая система с входными и выходными данными

Несколько смелых разработчиков очистили внутреннюю часть системы, получив вариант S' (рис. 1.2). Хотя код стал более аккуратным и упорядоченным, для пользователей S' абсолютно ничего не изменилось.

Рис. 1.2. Простая перепроектированная система с входными и выходными данными

Система S может выглядеть как угодно. Это может быть один оператор if, функция из десятка строк, популярная библиотека с открытым исходным кодом, огромное приложение или что-то среднее между ними. Такие же разнообразные формы способны принимать входные и выходные данные. Система может работать с записями базы данных, наборами файлов или потоками данных. На выходе она не просто возвращает значения, но может дополнительно выполнять такие действия, как вывод на консоль или сетевой запрос. На рис. 1.3 мы видим пример системы в виде RESTful-службы, отвечающей за работу с пользовательскими сущностями.

Рис. 1.3. Система на примере простого приложения

Постепенно мы будем развивать определение рефакторинга и перейдем к рассмотрению разных аспектов этого процесса. Убедиться, что мы одинаково понимаем происходящее, будет проще, связывая каждую идею с конкретным примером.

К сожалению, привести реальные примеры из сферы программирования сложно. Из-за многообразия вариантов выбор одного даст преимущество только определенной группе читателей. Причем тех, кто хорошо знаком с этой областью, может разочаровать упрощение концепций для краткости или игнорирование некоторых нюансов. И чтобы все были в равных условиях, покажем проблему в общем виде на примере знакомого всем бизнеса — химчистки.

Расположенная на оживленной улице Спрингфилда химчистка Саймона открыта с понедельника по субботу в рабочие часы и принимает вещи как в стирку, так и для химической чистки. Срок выполнения заказа зависит от количества вещей, срочности и сложности. Это может занять от двух до шести рабочих дней.

Как это связано с нашим определением системы? В данном случае система — это набор операций по чистке вещей. Входные данные — грязная одежда, которую сдают клиенты. Возвращаемая чистая одежда — результат работы системы. Все тонкости обработки скрыты от потребителя. Люди сдают грязную одежду и надеются на добросовестность служащих химчистки. При этом система довольно сложна. В зависимости от типа вводимых данных (кожаная куртка, груда носков, шелковая юбка) выполняются одна или несколько операций для обеспечения нужного результата (чистой одежды). В промежутке между сдачей и получением вещей может произойти много сбоев. Например, может потеряться ремень, останется незамеченным пятно, рубашку случайно отдадут другому клиенту. Но если сотрудники работают слаженно, машины исправны, а квитанции оформляются как положено, система будет бесперебойно работать и легко выполнять заказы.

Допустим, химчистка Саймона до сих пор работает с бумажными квитанциями. Каждый клиент указывает на бланке свое имя и номер телефона, а приемщик присваивает их заказу номер. Если клиент потеряет квитанцию, в химчистке могут легко найти копию, пролистав последние заказы, отсортированные по фамилиям в алфавитном порядке. Если клиент не только потерял квитанцию, но и вовремя не пришел за своим заказом, служащему приходится искать копию в архиве. Но даже в этом случае все еще можно получить чистые вещи, просто процедура выдачи займет больше времени. Бумажные квитанции неудобны и при подсчете дохода в конце каждого месяца, ведь приходится вручную сопоставлять все транзакции (как по кредитной карте, так и наличными) с выполненными заказами. Поэтому команда решает упростить рабочий процесс, перейдя от бумажного документооборота к системе кассовых терминалов. По завершении рефакторинга клиенты все еще будут оставлять грязные вещи и забирать их чистыми через несколько дней. Для них ничего не изменится, а вот для сотрудников химчистки процесс пойдет более гладко.

Что такое масштабируемый рефакторинг

В конце 2013 года все основные американские новостные агентства объявили, что запуск сайта Healthcare.gov, который был символом реформы здравоохранения, завершился полным фиаско. Множество дыр в безопасности, многочасовые отключения и огромное количество серьезных ошибок. Перед запуском оказалось, что стоимость выросла почти до двух миллиардов долларов, а кодовая база разрослась до более пяти миллионов строк кода. Провал Healthcare.gov был обусловлен неудачной разработкой, связанной с бюрократической политикой правительства, когда администрация Обамы объявила о планах вложить в улучшение сервиса значительные средства. Но основной проблемой стали неоспоримые трудности, связанные с изменением архитектуры огромного программного комплекса. В последующие месяцы команды, которым было поручено переписать Healthcare.gov, с головой погрузились в почти полную переработку кодовой базы — в масштабируемый рефакторинг.

Рефакторинг называется масштабируемым, если влияет на значительную часть системы. Обычно (но бывают и исключения) это означает большую кодовую базу (миллион или более строк) приложения с множеством пользователей. Пока есть старые системы, останется потребность в таких реорганизациях, когда разработчикам приходится переосмыслять всю структуру кода и искать способы ее эффективного улучшения.

В чем разница между рефакторингом огромной кодовой базы и рефакторингом небольшого, более четко определенного приложения? Для небольшой системы проще обнаружить итеративные способы улучшения (рассматривая отдельные функции или классы), но почти невозможно предсказать, как повлияет произвольное изменение на огромную сложную систему. Есть много инструментов для выявления проблем в структуре кода или автоматического обнаружения улучшений в отдельных фрагментах. Но как автоматизировать рассуждения людей на тему реструктуризации быстрорастущих приложений с огромной кодовой базой?

Можно возразить, что для совершенствования таких систем подойдут постоянные небольшие кумулятивные преобразования. Конечно, так мы пойдем в верном направлении, но их эффективность сильно упадет после выполнения простейших действий, и вносить изменения осторожно и постепенно станет сложнее.

Масштабируемый рефакторинг сводится к определению системной проблемы в кодовой базе, разработке лучшего решения и его стратегическому и упорядоченному внедрению. Для определения системных проблем и поиска их решений нужно хорошо понимать один или несколько основных разделов приложения. Много сил уйдет и на корректное распределение решения на всю пораженную область.

Масштабируемый рефакторинг тесно связан с рефакторингом работающих систем. Многие из нас работают над приложениями с частыми циклами развертывания. Новый код отправляется в Slack десять раз в день. Нужно корректно вписываться в эти циклы, чтобы минимизировать риски и неудобства пользователей. Без понимания того, как в процессе рефакторинга осуществлять стратегическое развертывание в разных точках, мы можем прийти к полному отключению сервиса.

Как будет выглядеть этот процесс на примере химчистки Саймона? Представим, что внедрение системы кассовых терминалов позволило всего за два года открыть пять новых точек в соседних городах! Теперь компания работает в нескольких местах, и у нее появились другие проблемы. Для снижения расходов оборудованием для химчистки снабдили всего два приемных пункта из шести. Поэтому из четырех пунктов одежду нужно отвезти в ближайший с оборудованием. Машина компании объезжает четыре точки и сбрасывает собранные вещи в большие ящики на погрузочных площадках двух химчисток. Сотрудники Саймона сортируют вещи, чистят их и возвращают в нужный пункт приема. Это непростой процесс. Обе химчистки обрабатывают вещи из собственного пункта приема и из четырех других. Нередко во время перекладывания из машины в ящики вещи спутываются. Из получившейся кучи приходится вручную доставать срочные заказы.

Как улучшить функционирование химчистки Саймона? Может, стоит купить оборудование для химчистки еще в один пункт, чтобы дополнительные заказы приезжали всего с трех? Как тогда должны поменяться маршруты машин? Сократит ли открытие еще одной химчистки время обработки заказов? Как сделать так, чтобы при перегрузке вещи не цеплялись друг за друга? Можно ли научить водителей сортировать вещи по срочности заказа? Может быть, лучше везти вещи в химчистку сразу после обеда и вскоре после закрытия, чтобы высвободить дополнительное время на их прием? Вариантов масса, и многие из них можно сочетать. Вопрос лишь в том, как выбрать самую выгодную стратегию. Это нелегкий выбор. Примерно так же выглядит рефакторинг больших приложений.

Зачем нужен рефакторинг

В теории рефакторинг выглядит интересно. Но как понять, не станет ли чтение этой книги пустой тратой времени? Я надеюсь, что каждый читатель сможет пополнить свой арсенал новыми инструментами, но есть и другие причины не откладывать книгу в сторону.

Уверенность в способности произвести рефакторинг позволяет приступить к созданию системы до того, как станут понятны все ее составляющие, подводные камни и пограничные случаи. Возможность эффективно улучшать компоненты во время разработки, которую используют по мере усложнения системы, избавляет от больших временных трат на предварительное планирование архитектуры программы. Отточенные навыки управления кодом позволят не ограничивать себя одним проектным решением. В процессе программирования лучше написать простой код, работающий в данных обстоятельствах, а не планировать на десяток шагов вперед.

Вы поймете, что всегда можно найти решение лучше, но не всегда это будет просто. Программирование не игра в шахматы, где при заданной конфигурации доски хорошие игроки могут за считаные минуты ловко разыграть несколько партий. К сожалению, в этой области никто не даст вам полный набор возможных ходов. Конечное состояние тоже не определено. Я не хочу сказать, что искать надежное решение поставленной задачи с учетом всех требований бессмысленно. Но не тратьте много времени на идеальную шлифовку последних 10–20 процентов. Имея навыки рефакторинга, вы сможете без проблем доработать решение с учетом окончательных специ­фикаций.

Выгоды рефакторинга

Рефакторинг не только позволяет решать задачи увереннее. У него есть и другие преимущества. Пусть этот инструмент подходит не для всех задач, в будущем он может положительно повлиять на приложение, команду разработчиков и организацию в целом. Рассмотрим два основных преимущества: повышение производительности труда разработчиков и упрощение поиска ошибок. Некоторые считают, что рефакторинг дает и другие преимущества. Но я утверждаю, что все они могут быть отнесены к одной из двух этих групп.

Продуктивность разработчика

Одна из основных целей рефакторинга — получение более простого кода. Это упрощение помогает понять, что делает код, вам и всем, кто будет иметь с ним дело потом. Новая версия кода повысит уровень абсолютно всех членов команды независимо от их опыта.

Обычно инженеры-разработчики хорошо знакомы с некоторыми частями кодовой базы. Но по мере ее роста незнакомых фрагментов становится больше. Кроме того, велика вероятность появления зависимостей.

Представьте, что в процессе внедрения новой функции вы переходите от хорошо знакомого кода на неизвестную территорию. Если эта кодовая база в хорошем состоянии и регулярно обновляется с учетом исправлений ошибок и меняющихся требований, идеальное место для внесения изменений вы найдете гораздо быстрее и быстрее разработаете простое решение. Может быть и так, что этот незнакомый вам код со временем разросся и стал хуже из-за частичного исправления ошибок. В таком случае придется просмотреть каждую его строку, чтобы сначала понять принцип его действия, и только после этого появится возможность перейти к поиску подходящего решения. Нередко бывает, когда к работе над этим истерзанным кодом привлекают кого-то еще. Например, коллегу или того, кто хорошо знаком с кодом и может отвечать на возникающие вопросы.

Эволюция знакомства с кодовой базой

Если кодовая база невелика и с ней работает небольшая команда, ее участники будут хорошо знакомы со всеми фрагментами кода. По мере добавления и изменения новых модулей степень знакомства будет уменьшаться. Постепенно члены команды начнут специализироваться на отдельных частях. В итоге наступит момент, когда знать весь код не сможет ни один сотрудник (даже если он работает с начала проекта!).

Представьте, что коллеге из другой команды придется читать ваш код. Сможет ли он легко понять, как все работает? Или вы ожидаете недоуменных взглядов и просьбы проанализировать код?

Новый человек в команде не знает, как выглядит кодовая база, с которой ему предстоит работать. Способность разобраться в любой части кода напрямую зависит от его читабельности. Чем он аккуратнее, тем проще новичку определить взаимоотношения между модулями и понять, что делает код, не беспокоя своими вопросами остальных.

Впрочем, нужно понимать, когда и как можно задавать вопросы своим коллегам. Это невероятно важный навык, который тоже следует оттачивать. Научиться оценивать, сколько времени вам нужно, чтобы сформировать свое понимание, прежде чем обращаться за помощью, сложно, но важно для вашего роста как разработчика. В вопросах менее опытных коллег нет ничего плохого. Но если вы штатный инженер-разработчик и вас просто засыпают вопросами, возможно, пришло время написать документацию и провести рефакторинг кода.

При разработке чего-то нового люди склонны пользоваться базовыми шаблонами. Если они будут ясными и краткими, более вероятно, что новый код получится четким и сжатым. Верно и обратное: если вы ориентируетесь на громоздкие решения, вы будете и дальше плодить громоздкий код. Нужно обеспечить положительную обратную связь для начинающих разработчиков. Если код, с которым они регулярно взаимодействуют, будет простым для понимания, они будут придерживаться такого же в собственных решениях.

Обнаружение ошибок

Выявление и устранение ошибок — важная (и увлекательная!) часть нашей работы. Эффективным инструментом для решения обеих задач может стать рефакторинг. В процессе разбиения сложных операторов на более мелкие фрагменты и превращения алгоритмов в новые функции можно не только лучше понять, что делает код, но и найти ошибку. Рефакторинг, проводимый в процессе активного написания кода, упрощает выявление ошибок на ранних этапах разработки. Это позволяет полностью их избежать.

Представим, что несколько часов назад введен в эксплуатацию новый код. Некоторые внесенные изменения находятся в файлах, которые все опасаются редактировать. Код в этих файлах очень сложно читается и имеет множество подводных камней. К сожалению, в процессе тестирования обновлений не рассматривался ни один из пограничных случаев. Из службы поддержки клиентов сообщают об ошибке, с которой начинают сталкиваться пользователи. Рабочая группа быстро понимает, что ошибка, как это часто бывает, скрывается в той самой «страшной» части кода. К счастью, одному из членов группы удается последовательно воспроизвести ошибку. Вместе вы пишете тест, подтверждающий, что при таких обстоятельствах она действительно возникает. Теперь ошибку нужно локализовать. Вы систематически разбиваете сложный код на более мелкие фрагменты. Конвертируете длинные однострочные выражения в сжатые многострочные операторы и переносите содержимое нескольких условных блоков кода в отдельные функции. Наконец ошибка обнаружена. Теперь код стал проще, и ее можно быстро исправить, протестировать и отправить исправление клиентам.

Иногда ошибки становятся лишь досадной неприятностью, но бывают случаи, когда они блокируют возможность пользоваться приложением. Чем серьезнее ошибка, тем более оперативно на нее нужно реагировать. Но для удобства пользователей команда должна уметь быстро исправлять любые ошибки. Хорошее состояние кодовой базы сильно сократит время поиска и исправления ошибки, радуя пользователя выпуском обновления в ре­кордно короткий срок.

Риски рефакторинга

Несмотря на очевидные преимущества рефакторинга, есть серьезные риски и подводные камни, которые нужно учитывать до начала работы по улучшению кодовой базы. Хотелось бы повториться: рефакторинг требует гарантированного сохранения одинакового поведения на каждой итерации. Удостовериться в отсутствии изменений можно, написав набор тестов (для отдельных модулей, для сопряжений, сквозных). Поэтому бессмысленно прилагать какие-либо усилия к началу рефакторинга, пока не достигнут достаточный тестовый охват. Но даже при тщательном тестировании всегда остается вероятность того, что мы что-то не заметим. Кроме того, всегда нужно помнить о конечной цели: улучшить код так, чтобы он стал понятным и вам, и всем, кто будет с ним работать.

Риск регрессии

Рефакторинг непротестированного кода очень опасен, поэтому крайне не рекомендуется его проводить. Почему команды разработчиков, оснащенные самыми высококачественными и продуманными пакетами для тестирования, все еще отправляют в эксплуатацию код с ошибками? Любое изменение нарушает равновесие системы. Несмотря на стремление минимизировать это нарушение, риск регрессии все равно есть. Особенно об этом нужно беспокоиться при рефакторинге запутанных фрагментов кодовой базы. Часто их состояние такое из-за того, что ими долго никто не занимался. Они сплошь и рядом попадаются в приложениях быстрорастущих компаний и реже всего тестируются. Попытка как-то причесать такие файлы или функции напоминает прогулку по минному полю. Пройти его можно, но это очень опасно.

«Спящие» ошибки

Рефакторинг позволяет выявлять не только существующие, но и «спящие» ошибки. К этой категории я причисляю и регрессию, возникающую при реструктуризации кода. Рассмотрим это явление на примере химчистки Саймона. Для получения скидки поставщика компания начала заказывать чистящие средства большими партиями. Но в офисе мало места для хранения, поэтому их начали складывать рядом с погрузочной платформой. Однажды несколько дней шел дождь, и оказалось, что ближайшие к двери коробки разваливаются от сырости, так как задняя дверь закрывается неплотно и пропускает воду. С такой проблемой в химчистке никогда не сталкивались, потому что раньше рядом с погрузочной платформой ничего не было. Изменение схемы хранения выявило критический недостаток в инфраструктуре, который в иных обстоятельствах никогда бы не обнаружили.

Неконтролируемый рост проекта

Чем-то рефакторинг похож на поедание пирожных: их вкус так восхитителен, что легко увлечься и съесть целую дюжину. Но проглотив последний кусок, вы начинаете испытывать сожаление. Наблюдать, как вашими усилиями улучшается код, очень приятно! Легко увлечься и не заметить, как число изменений выходит за разумные пределы. Разумность этих пределов зависит от кодовой базы. Это может быть как одна функция, так и небольшой взаимозависимый набор библиотек. В идеале рефакторинг кода желательно ограничить редактированием, которое другой разработчик может легко просмотреть в рамках одного набора изменений.

При планировании более крупного рефакторинга, особенно такого, который может занять несколько месяцев, необходимы жесткие рамки. Все мы сталкивались с неожиданностями при рефакторинге небольших фрагментов (несколько строк кода, отдельные функции). Можно внести ряд улучшений и эффективно справиться с этими странностями, но при работе с большим массивом кода такой подход становится опасным. Чем больше площадь планируемого рефакторинга, тем с большим количеством неожиданных проблем вы столкнетесь. Это не значит, что вы плохой программист. Просто человеческие возможности не безграничны. Поэтому для уменьшения возможности серьезного регресса или столкновения со «спящими» ошибками и повышения производительности нужно придерживаться четкого плана. Долговременный систематический рефакторинг сложен сам по себе. А если в процессе еще начать менять правила игры, его проведение станет невозможным.

Ненужное усложнение

Не тратьте много времени на проектирование вначале. Более того, нужно быть готовым менять планы. Основная цель рефакторинга — создание удобного для человека кода, пусть даже за счет исходного проекта. Если сосредоточиться не на процессе, а на желаемом результате, велика вероятность, что приложение окажется более надуманным и сложным, чем было изначально. Рефакторинг должен быть итеративным на всех уровнях. Небольшие осознанные шаги в одном направлении и поддержка существу­ющего поведения на каждой итерации позволяют лучше сосредоточиться на цели. Этого намного проще достичь, если брать на себя столько кода, сколько умещается на экране, а не три десятка библиотек за раз. При планировании нового проекта многие стараются разработать подробную спецификацию и план работы. И даже если для рефакторинга нужно много усилий, важно понимать, как должен выглядеть код по его завершении.

Когда начинать рефакторинг

На этот вопрос можно ответить так: когда выгода от него перевешивает риски. Но такой ответ совершенно неинформативен — непонятно, как просчитать выгоды и риски для множества частей сложной системы. Как же понять, что наступил критический момент, когда без рефакторинга уже не обойтись?

По моему опыту, «критический момент» на самом деле — это временной интервал, индивидуальный для каждого приложения. Определение верхней и нижней границ этого интервала вносит в вопрос о рефакторинге толику субъективности. Формулы, которой можно было бы воспользоваться и однозначно узнать «да» или «нет», не существует. К счастью, принять решение можно, опираясь на уже имеющиеся эмпирические данные.

Небольшой масштаб

Почти никто не мешает провести рефакторинг небольшого несложного фрагмента хорошо протестированного кода. Если вы не сомневаетесь, что новое решение будет лучше старого, и не боитесь, что изменение затронет слишком большую часть кода, то стоит попробовать. Тщательно выполните несколько коммитов — и пусть новый код работает! Далее в главе вы найдете пример, попадающий под эту категорию.

Сложность кода мешает активному развитию

Встречается код, при чтении которого портится настроение и поднимается давление. Но рано или поздно наступает момент, когда, сжав зубы, приходится вносить в него запланированные изменения. Такое вынужденное редактирование только прибавит вам проблем. Когда человек концентрируется на том, чтобы следовать плану, одновременно удерживая в голове множество аспектов проблемы, есть риск упустить из виду настоящую цель. Как достичь реальной цели, если ум занят другим?

Если конкретный фрагмент кода еще не успел причинить вреда, мы часто идем на риск и проводим его рефакторинг. Но когда вред уже причинен (и, возможно, неоднократно), редактировать такой код для предотвращения будущих ошибок может быть более рискованно, чем оставить его в нынешнем состоянии. Если вы не уверены, обсудите ситуацию с коллегами и соберите данные о количестве ошибок за последние полгода, которые можно связать с этой частью кодовой базы.

Изменение требований к продукту

Радикальные сдвиги в требованиях к продукту часто становятся причиной кардинального редактирования кода. Даже если приложить массу усилий к написанию абстрактных расширяемых решений для любой функциональности приложения, будущее предсказать невозможно. Код, легко поддающийся адаптации в случае небольших отклонений, редко идеально адаптируется к более крупным. Зато мы получаем редкую возможность начать все с чистого листа и пересмотреть проект.

Вы можете подумать, что такого рода сдвиги никак не могут сохранить поведение. Ведь с теми же входными данными теперь нужен совсем другой результат! Разве это подходящее время для рефакторинга? Если текущий код не соответствует новым требованиям, нужно предложить решение, которое продолжит поддерживать имеющуюся функциональность и позволит легко добавлять новые. Можно сначала провести рефакторинг кода, а после (и  только после!) реализовать новые функции поверх него. В итоге вы внедрите стандарт высококачественного кода, извлечете выгоду из рефакторинга и поддержите коммерческие цели. Что нам и требуется.

Производительность

Повысить производительность непросто. Сначала нужно детально разобрать существующее поведение, а после определить, с помощью каких рычагов можно склонить чашу весов в нужном направлении. Такие вещи проще всего проделывать с чистого листа (или создав такой лист в качестве первого шага). Определенные вами рычаги важно правильно изолировать, чтобы манипуляция ими происходила без побочных эффектов.

Не все разработчики считают повышение производительности причиной для рефакторинга. Некоторые утверждают, что производительность системы неразрывно связана с ее поведением. Иначе говоря, ее изменение меняет поведение. Я с этим не согласна. Если мы продолжим определять рефакторинг через нашу обобщенную систему, для которой мы предоставляем входные данные, и в результате все равно получаем ожидаемый набор выходных данных, то повышение скорости их генерации — допустимая форма рефакторинга.

Уникальность рефакторинга для повышения производительности в том, что код в результате не становится понятнее. Иногда в кодовой базе можно наткнуться на длинный блок комментариев с предупреждением. По моему опыту, в большинстве случаев это предупреждение о подводных камнях: странном поведении приложения, временном обходном пути и своеобразном способе повышения производительности. Большая часть повышающего производительность кода написана грамотно и с глубоким пониманием кодовой базы. Но сделано это так, чтобы минимизировать затрагиваемую поверхность. Такие «улучшения» деградируют гораздо быстрее, так что речи об устойчивости, которой призван способствовать рефакторинг, тут не идет. Примеры увеличения производительности, которые попадают под определение рефакторинга, всегда глубоки и разветвлены. Это примеры эффективного масштабируемого рефакторинга. Подробно мы рассмотрим их во второй части.

Переход к новой технологии

В мире разработки программного обеспечения регулярно внедряются новые технологии. Неважно, что нами движет — желание идти в ногу с новейшими тенденциями в отрасли, масштабировать решение для множества пользователей или усовершенствовать продукт новым способом, — мы постоянно оцениваем новые библиотеки с открытым исходным кодом, протоколы, языки программирования, поставщиков услуг и т.д. Решения воспользоваться чем-то новым принимаются нелегко. Отчасти из-за стоимости интеграции в существующую базу кода. Если мы предпочтем полностью заменить существующее решение, придется разработать план депрекации, идентифицировав все задействованные места вызовов и перенеся их (иногда по одному). Если же новую технологию планируется внедрить в будущем, нужно определить перспективные технологии, составив план расширения их применения для всех возможных сценариев использования.

Я не буду перечислять все варианты влияния новой технологии на систему (их очень много). Из написанного выше и так ясно, что каждый раз нужен тщательный анализ состояния существующей системы. В процессе могут появиться отличные возможности для рефакторинга! Хотя на этот счет есть и другое мнение. Многие разработчики считают, что внедрение новой технологии уже достаточно рискованно и добавление еще каких-то изменений будет лишним. В этом есть рациональное зерно. Нет ничего хуже, чем добавлять новшества в запутанную систему. Переход к новой технологии будет проще, если сначала привести код в порядок.

Этот подход легко применить к химчисткам Саймона. Допустим, туда было заказано современное экологичное оборудование. При составлении плана его установки выясняется, что уже имеющееся оборудование размещено нерационально. Чтобы забрать отсортированную одежду со стеллажей, сотрудникам приходится пройти мимо ряда машин почти 10 метров. Перестановка оборудования поможет сократить каждый цикл на несколько минут. В результате новые машины расставляются иначе. Это позволяет не только снизить воздействие на окружающую среду, но и повысить производительность сотрудников.

Когда не нужно делать рефакторинг

Рефакторинг может стать удивительно полезным. Поэтому многие разработчики считают, что время, потраченное на него, потрачено не зря. На самом деле все не так просто. Для рефакторинга важно правильно выбрать место и время. В этом разделе вы узнаете, когда его делать не стоит.

Для забавы или со скуки

Закройте на минуту глаза и представьте, что смотрите на очень сложную функцию. Она слишком длинная, пытается одновременно делать слишком много, ее название никак не отражает ее суть.

Вероятно, вы захотите ее исправить. Разбить на четко определенные лаконичные фрагменты, дать переменным осмысленные имена. Это было бы так увлекательно! Но разве у вас сейчас нет дел поважнее? Возможно, коллега уже несколько дней ждет от вас обзора кода или все это время вы откладывали написание тестов? Копаясь для развлечения в старом, выведенном из эксплуатации коде и редактируя его, вы вредите себе (и товарищам по команде).

Обычно, когда рефакторинг проводится для развлечения, можно не обращать особого внимания на то, как вносимые изменения повлияют на остальной код, систему в целом и на работу коллег. Скорее всего, будут добавляться надуманный функционал или испытываться новые шаблоны. Пробовать что-то новое и отрабатывать навыки программирования все-таки лучше отдельно от рефакторинга. Он должен быть осознанной процедурой, где нужно стараться внести больше улучшений с минимальным количеством изменений.

Вы случайно проходили мимо

Представьте, что через несколько месяцев вы вернулись к своему коду, чтобы расширить функционал. Но становится ясно, что он совсем не похож на то, что вы писали. Возможно, вы стали жертвой реформатора. Обычно это достаточно опытный сотрудник, хорошо понимающий, как писать код. Инженеры-разработчики консультируются с ним по поводу проектных решений. Но у них есть склонность переписывать чужой код, попадающий им в руки. Такие сотрудники думают, что так будет лучше.

Возможно, вы захотите согласиться с внесенными изменениями, но есть один нюанс. Чужие правки часто снижают производительность ответственных за код. Хорошее знакомство с кодом обеспечивает наибольшую продуктивность. Когда нужно быстро решить проблему, мы используем мысленную модель кода, чтобы сузить набор файлов, классов или функций, в которые могла вкрасться ошибка. А потом открываем редактор и видим там совершенно другую картину. Понятно, что об оперативном устранении проблемы уже не может быть и речи. За чужой рефакторинг приходится платить увеличением времени работы технического персонала и службы поддержки, а иногда и потерей бизнеса.

Не уведомить автора кода о проведенном рефакторинге — значит повести себя непорядочно по отношению к нему. Во-первых, это открытое выражение недоверия. Я бы предпочла, чтобы мне перечислили недостатки моего кода и подсказали, как их исправить, а не уведомляли постфактум, что кто-то за моей спиной устранил возникшие проблемы. Особенно обидно в такой ситуации начинающим инженерам. Представьте себе вчерашнего выпускника, который неделю трудился над кодом, а потом внезапно обнаружил, что кто-то из старших коллег, ни слова не сказав, внес туда правки.

Во-вторых, вряд ли такому стороннему любителю рефакторинга известны исходные обстоятельства появления кода. Почему их нужно знать? Программирование всегда связано с компромиссами. Например, можно получить решение, которое работает быстрее за счет структуры данных с большим объемом памяти. Или наоборот, уменьшить потребление памяти, заменив точные вычисления приблизительными. Я хочу сказать, что каждая строчка «плохого» кода направлена на решение какой-то задачи. Проведя рефакторинг наобум, можно пасть жертвой ошибки или уязвимости, которых авторы исходного кода тщательно пытались избежать.

Об авторах кода

К сожалению, не каждый может проработать в одной компании всю жизнь. Бывает, что с автором исходного кода невозможно поговорить, потому что он здесь больше не работает. К счастью, прежде чем уйти, люди обычно передают информацию своим коллегам. Так что узнать о коде подробнее, вероятно, вы сможете у коллег создателя. Если за вашим желанием провести рефакторинг стоят благие намерения, как можно реже прикасайтесь к коду, в поддержке которого вы не принимаете активного участия. Но если вы все же решили это сделать, обязательно привлеките к процессу ответственных за этот код.

Чтобы обеспечить расширяемость кода

Многие специалисты по рефакторингу советуют эту процедуру как способ получения более расширяемого кода. Такой код действительно можно получить с помощью рефакторинга. Но переписывать его ради гибкости в будущем неразумно. Без четкого понимания дальнейшего результата вы можете зря потратить время на рефакторинг, и внесенные изменения не окупятся за все время эксплуатации кода.