Самый большой рефакторинг за всю карьеру включал систему примерно на 10к строк.
Это был симулятор бильярда — целый режим игры с огромными системами внутри. Задача — добавить новый режим.
Но просто взять и начать писать было невозможно:
▫️ Режимом управлял один класс на 2000 строк. Методы вызывали друг друга, промежуточное состояние записывалось в приватные поля.
▫️ Сам класс хоть и был за интерфейсом, но оставался неразрывно связан с другими частями системы.
Сеть, при изменении состояния, двигала игру дальше — и механизм требовал четкого порядка вызовов.
Потому сеть сама сортировала события, которые отдавала, чтобы логика работала правильно.
Это как раз пример content и control coupling'ов:
▫️Content coupling — один модуль напрямую обращается к внутренним данным другого или модифицирует их
▫️Control coupling — один модуль передает другому управляющую информацию (флаг, параметр), определяя что тот должен делать
И прежде чем добавлять что-то новое — нужно снизить сложность старого.
Противовес локальной и глобальной сложности — модульность.
У этого решения есть отдельное большое обоснование, которому я на курсе посвятил лекцию на 3 часа.
В текстовом формате не хватит и 5 постов, так что ждите анонса онлайн версии курса — уже в Мае 😎
🔸Мое решение:
1️⃣ Не меняя логику, разбить систему на части. Выносить куски кода в отдельные классы по функциональности — буквально ctrl+c, ctrl+v в новые файлы.
2️⃣ Ни при каких обстоятельствах не менять порядок вызовов.
🔹Почему п.2 критически важен:
При внесении изменений всегда нужно стараться избегать увеличения регресса.
Регресс — тип ошибки, при которой ранее работавшая функциональность перестаёт работать корректно после внесения изменений в код.
Для себя я формулирую это так:
▫️Регресс — это дополнительный объем работы для QA, чтобы убедиться в работоспособности твоих изменений
▫️А если глубже — это изменение кол-ва вариантов выполнения кода, которые делают возможным исполнение старых, уже исправленных багов
И самый надежный способ его контролировать — переносить куски кода в другие файлы без модификации.
🔸Звучит странно
Да. Но у этого есть конкретное назначение:
Снижение сложности без увеличения количества багов — суть рефакторинга.
На домашних проектах это может показаться излишним. На больших — это реальность:
▫️Сроки поджимают, и неидеальный код внутри не так страшен, как неработающая версия
▫️Регресс может быть настолько огромным, что одно упоминание для QA и PO станет аргументом вообще не брать задачу в работу.
Ее оценят в кучу дней, PM закинет в бэклог, а разработчик будет вкостыливать новый режим в сжатые сроки.
🔸Как рефакторить огромные системы
1️⃣ Разбиваем на части без изменения логики — снижвем сложность и контролируем регресс
2️⃣ Отдаем QA на проверку
3️⃣ Рефакторим разбитую систему по частям — меняем логику по кусочкам, проверяем что не тригернули старых багов
🔸Отдельная критическая ошибка
Часто вижу как вместе с фиксом бага на ревью отправляют рефакторинг каких-то частей.
Мысли разработчика: "Ну, заодно вот еще улучшил это место."
Часто оно правда так. Но не менее часто — изменения могут потребовать регресса измененной части. А это время и риски для того, кто ответственен за релиз.
🔻 Всегда держи в голове не только благую мысль "я делаю проект лучше", но и ответственность за дополнительную работу, которую ты создаешь другим людям. Истина — в балансе. И объем регресса, который ты создаешь — отличная метрика этого баланса.
Ставь 👍 если тебе заходит такого рода контент!
Ты знаешь кому переслать эту статью 💪
#проект_в_разработке@UniArchitect
