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

Как рефакторить код, чтобы на утро не болела голова.

Привет! 👋

Меня зовут Саша Беспоясов, я работаю консультантом в 0+X.
Пишу код больше 10 лет. Веду технический блог, пишу о разработке.

Сегодня я расскажу о том, как эффективно рефакторить код и какие из программистских базвордов для этого понадобятся.

Дисклеймер

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

Рефакторинг — изменение внутренней структуры программы, не затрагивающее её внешнего поведения для упрощения понимания её работы.

В чём польза разработчикам

Рефакторинг требует усилий, но взамен мы получаем:

В чём польза бизнесу

«Плохой» код

Характеристики субъективны, их сложно измерить напрямую. Но все они отсылают к тому, «насколько код читаем».

...Если постараться, то измерить характеристики, конечно, можно, но об этом позже 🤫

Костыли для определения «плохого» кода

Когнитивные костыли, которые говорят, как узнать «плохой» код.

Запахи кода

Иногда достаточно посмотреть на список запахов, найти подходящий и использовать рецепт против него.

Программистские словечки™

💩 → 🍬

От простого к сложному

Прежде, чем начать

Не забывать

Все примеры на Гитхабе!

Изучите историю коммитов. В ней я показал, как применять разные техники и использовать словечки™, чтобы находить проблемные места.

Пример сообщения о коммите, где рассказано, что и зачем сделано с кодом

Форматирование

Применение форматтера к куску кода

Линтеры

Как минимум помогут найти «мёртвый» код и неиспользуемые переменные.

Линтер указывает на неиспользуемую переменную

Возможности языка

Тернарник иногда можно заменить на Math.min
Сериализация на FormData работает также, но занимает меньше места и лучше расширяется

Возможности браузера

Можно избавиться от лишнего кода, если использовать возможности языка

Возможности среды

Используйте автоматизированные средства рефакторинга
...Но следите за изменениями, которые они делают

Короткие имена и сокращения

Избегайте сокращений, в них слишком мало контекста

Чрезмерно длинные имена

Не перегружайте имя лишними подробностями

Вариант именования

Паттерн A/HC/LC Pattern предполагает использовать максимум два контекста на одну сущность

Следить за контекстом

Если информация доступна из близкого контекста, её можно опустить. Но лучше с этим быть аккуратнее.

Если информация доступна из контекста, её можно опустить

Одинаковые имена у разных сущностей

Избегайте одинаковых имён для разных сущностей

Повсеместный язык

Используйте понятия из предметной области
Моделируйте домен в типах

Функции и дедупликация

Выделяйте повторяющиеся действия в функции

Функции и абстракция

Абстрагируйте детали реализации

Сложность не исчезает

Но с ней проще справляться.

Со сложностью проще справляться, когда детали реализации абстрагированы за понятным названием

Всё как в жизни

Абстрагирование помогает работать со сложными концепциями

Абстрагируйте детали

Абстрагируйте детали реализации

Разделение ответственности

Сущность должна отвечать за что-то одно

Функциональный пайплайн

Фокусируйтесь на преобразованиях данных. Считайте их состояния результатами бизнес-событий.

Фокусируйтесь на состояниях и преобразованиях данных как результатах бизнес-событий

Разделяем состояния

Если выделить этапы жизненного цикла заказа, станет понятно, что тут на самом деле две функции

Добавляем состояния

В функциональный пайплайн проще добавлять новые преобразования

Условия

Плоские условия

Выносите повторяющиеся условия в переменные.

Закономерности упрощайте правилами булевой логики.

!(a && b) === !a || !b
!(a || b) === !a && !b

Выносите повторяющиеся условия в переменные — это поможет увидеть закономерности

Ранний return

Ранний return помогает «развернуть» сложные условия

Предикаты

Предикаты абстрагируют детали проверки, оставляя суть декларативной

Immutable by default

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

Ссылочная прозрачность

Чистые функции — это отличный пример абстракции. Если вызов функции можно заменить её результатом, то мы можем «абстрагировать» её детали реализации.

Predicates abstract the details of verification, leaving the code declarative

Сайд-эффекты и обработка ошибок

Почипо-бутерброд

Почипо, потому что:

  • побочный эффект.
  • чистые преобразования.
  • побочный эффект.
Сайд-эффекты окружают преобразования из чистых функций

Почипо-группирование

Почипо помогает собрать код бизнес-правил в центре, а побочные эффекты — по краям.

Группировка по методу почипо помогает собрать весь важный код в одном месте

Команды и запросы, CQS

Избегайте смешения сайд-эффектов и возврата значения, применяйте command-query separation, CQS

«Невыразимые» невалидные состояния

Системы типов помогут подсветить проблемы с инкапсуляцией, невалидными состояниями данных и нарушением CQS.

Если вы используете статически-типизированный язык, постарайтесь сделать невалидные состояния «запрещёнными», чтобы их было невозможно получить «по ошибке»

Ошибки

Result-контейнеры

Контейнер — это коробочка, внутри которой находится результат работы какой-то небезопасной функции

Абстрагируйте «служебный» код

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

Проблемы слабой архитектуры

Когда модули сильно зацеплены, программу сложно расширять

Слои и направление зависимостей

Бизнес-правила не должны зависеть от внешних сервисов
В коде мы реализуем такое поведение с помощью адаптеров

Адаптеры и тестируемость

Расцепленный код проще тестировать, потому что легче подменять зависимости

Разделение UI и логики

Компонент, в котором намешано всё на свете сложно понимать, тестировать и изменять.

Отделяйте UI-логику от доменной

Декларативность

Рассказывайте что код должен делать, а не как.

Абстрагирование помогает коду быть декларативным

Ошибки императивности

В декларативном стиле тяжелее допустить ошибку

Иногда не прокатывает 🙄

// 🐌
array.map(x => x > 0.5);

// 🐇
for (const x of array) {
    x > 0.5;
}
            

«Сколько времени нужно, чтобы...»

Хот-тейки

Техники и рецепты

Саша Беспоясов 👋