Навичка рефакторингу коду
Систематичний підхід до рефакторингу коду на основі книги Мартіна Фаулера Refactoring: Improving the Design of Existing Code (2-ге видання). Ця навичка наголошує на безпечних, інкрементальних змінах, підкріплених тестами.
«Рефакторинг — це процес зміни програмної системи таким чином, що не змінює зовнішню поведінку коду, але покращує його внутрішню структуру.» — Мартін Фаулер
Основні принципи
- Збереження поведінки: Зовнішня поведінка повинна залишатися незмінною
- Малі кроки: Робити крихітні, тестовані зміни
- Тест-орієнтованість: Тести — це страхувальна сітка
- Безперервність: Рефакторинг — постійний процес, а не одноразова подія
- Співпраця: Затвердження користувача потрібне на кожній фазі
Огляд робочого процесу
Фаза 1: Дослідження та аналіз
↓
Фаза 2: Оцінка покриття тестами
↓
Фаза 3: Виявлення запахів коду
↓
Фаза 4: Створення плану рефакторингу
↓
Фаза 5: Інкрементальна реалізація
↓
Фаза 6: Перегляд та ітераціяФаза 1: Дослідження та аналіз
Цілі
- Зрозуміти структуру та призначення кодової бази
- Визначити обсяг рефакторингу
- Зібрати контекст про бізнес-вимоги
Запитання до користувача
Перед початком уточніть:
- Обсяг: Які файли/модулі/функції потребують рефакторингу?
- Цілі: Які проблеми ви намагаєтесь вирішити? (читабельність, продуктивність, супровідність)
- Обмеження: Чи є зони, які НЕ слід змінювати?
- Тиск термінів: Чи блокує це іншу роботу?
- Стан тестів: Чи існують тести? Чи проходять вони?
Дії
- Прочитати та зрозуміти цільовий код
- Виявити залежності та інтеграції
- Задокументувати поточну архітектуру
- Зафіксувати існуючі маркери технічного боргу (TODOs, FIXMEs)
Вивід
Представити знахідки користувачу:
- Резюме структури коду
- Виявлені проблемні зони
- Початкові рекомендації
- Запросити затвердження для продовження
Фаза 2: Оцінка покриття тестами
Чому тести важливі
«Рефакторинг без тестів — як їзда без пасків безпеки.» — Мартін Фаулер
Тести — ключовий засіб безпечного рефакторингу. Без них ви ризикуєте внести помилки.
Кроки оцінки
Перевірити наявні тести
bash# Пошук файлів тестів find . -name "*test*" -o -name "*spec*" | head -20Запустити існуючі тести
bash# JavaScript/TypeScript npm test # Python pytest -v # Java mvn testПеревірити покриття (якщо доступно)
bash# JavaScript npm run test:coverage # Python pytest --cov=.
Точка рішення: Запитати користувача
Якщо тести існують та проходять:
- Перейти до Фази 3
Якщо тести відсутні або неповні: Представити варіанти:
- Спочатку написати тести (рекомендовано)
- Додавати тести інкрементально під час рефакторингу
- Продовжити без тестів (ризиковано — потребує підтвердження користувача)
Якщо тести не проходять:
- ЗУПИНИТИСЯ. Виправити тести перед рефакторингом
- Запитати користувача: Чи слід спочатку виправити тести?
Рекомендації щодо написання тестів (якщо потрібно)
Для кожної функції, що рефакториться, забезпечити тести для:
- Успішний шлях (нормальна робота)
- Граничні випадки (порожні введення, null, межі)
- Сценарії помилок (невалідні введення, виключення)
Використовуйте цикл «red-green-refactor»:
- Написати тест, що не проходить (red)
- Зробити так, щоб пройшов (green)
- Рефакторити
Фаза 3: Виявлення запахів коду
Що таке запахи коду?
Симптоми глибших проблем у коді. Це не помилки, а індикатори того, що код можна покращити.
Типові запахи коду для перевірки
Див. references/code-smells.md для повного каталогу.
Короткий довідник
| Запах | Ознаки | Вплив |
|---|---|---|
| Довгий метод | Методи > 30-50 рядків | Важко зрозуміти, тестувати, супроводжувати |
| Дубльований код | Та сама логіка в кількох місцях | Виправлення помилок потрібне в кількох місцях |
| Великий клас | Клас з занадто багатьма відповідальностями | Порушує принцип єдиної відповідальності |
| Заздрість до функцій | Метод використовує дані іншого класу більше | Погана інкапсуляція |
| Одержимість примітивами | Надмірне використання примітивів замість обʼєктів | Відсутні доменні концепції |
| Довгий список параметрів | Методи з 4+ параметрами | Складно викликати правильно |
| Групи даних | Ті самі елементи даних зʼявляються разом | Відсутня абстракція |
| Оператори Switch | Складні ланцюжки switch/if-else | Важко розширювати |
| Спекулятивна загальність | Код «на всякий випадок» | Зайва складність |
| Мертвий код | Невикористаний код | Плутанина, тягар супровідності |
Кроки аналізу
Автоматичний аналіз (якщо скрипти доступні)
bashpython scripts/detect-smells.py <file>Ручний перегляд
- Систематично пройти код
- Зафіксувати кожен запах з розташуванням та серйозністю
- Категоризувати за впливом (Критичний/Високий/Середній/Низький)
Пріоритезація Зосередитися на запахах, які:
- Блокують поточну розробку
- Спричиняють помилки або плутанину
- Впливають на найчастіше змінювані шляхи коду
Вивід: Звіт про запахи
Представити користувачу:
- Список виявлених запахів з розташуванням
- Оцінку серйозності для кожного
- Рекомендований порядок пріоритету
- Запросити затвердження пріоритетів
Фаза 4: Створення плану рефакторингу
Вибір рефакторингів
Для кожного запаху обрати відповідний рефакторинг з каталогу.
Див. references/refactoring-catalog.md для повного списку.
Відповідність запахів рефакторингам
| Запах коду | Рекомендований рефакторинг |
|---|---|
| Long Method | Extract Method, Replace Temp with Query |
| Duplicated Code | Extract Method, Pull Up Method, Form Template Method |
| Large Class | Extract Class, Extract Subclass |
| Feature Envy | Move Method, Move Field |
| Primitive Obsession | Replace Primitive with Object, Replace Type Code with Class |
| Long Parameter List | Introduce Parameter Object, Preserve Whole Object |
| Data Clumps | Extract Class, Introduce Parameter Object |
| Switch Statements | Replace Conditional with Polymorphism |
| Speculative Generality | Collapse Hierarchy, Inline Class, Remove Dead Code |
| Dead Code | Remove Dead Code |
Структура плану
Використовуйте шаблон templates/refactoring-plan.md.
Для кожного рефакторингу:
- Ціль: Який код зміниться
- Запах: Яку проблему вирішує
- Рефакторинг: Яку техніку застосувати
- Кроки: Детальні мікрокроки
- Ризики: Що може піти не так
- Відкат: Як скасувати за потреби
Поетапний підхід
КРИТИЧНО: Впроваджуйте рефакторинг поступово, фазами.
Фаза A: Швидкі перемоги (Низький ризик, висока цінність)
- Перейменування змінних для ясності
- Витяг очевидного дубльованого коду
- Видалення мертвого коду
Фаза B: Структурні покращення (Середній ризик)
- Витяг методів з довгих функцій
- Введення обʼєктів параметрів
- Переміщення методів до відповідних класів
Фаза C: Архітектурні зміни (Вищий ризик)
- Заміна умовних конструкцій поліморфізмом
- Витяг класів
- Введення патернів проєктування
Точка рішення: Представити план користувачу
Перед реалізацією:
- Показати повний план рефакторингу
- Пояснити кожну фазу та її ризики
- Отримати явне затвердження для кожної фази
- Запитати: «Чи продовжити з Фазою A?»
Фаза 5: Інкрементальна реалізація
Золоте правило
«Зміна → Тест → Зелений? → Коміт → Наступний крок»
Ритм реалізації
Для кожного кроку рефакторингу:
Попередня перевірка
- Тести проходять (зелені)
- Код компілюється
Зробити ОДНУ малу зміну
- Дотримуватися механіки з каталогу
- Тримати зміни мінімальними
Верифікація
- Негайно запустити тести
- Перевірити на помилки компіляції
Якщо тести проходять (зелені)
- Закомітити з описовим повідомленням
- Перейти до наступного кроку
Якщо тести не проходять (червоні)
- ЗУПИНИТИСЯ негайно
- Скасувати зміну
- Проаналізувати, що пішло не так
- Запитати користувача, якщо незрозуміло
Стратегія комітів
Кожен коміт має бути:
- Атомарний: Одна логічна зміна
- Оборотний: Легко відкатити
- Описовий: Зрозуміле повідомлення коміту
Приклади повідомлень комітів:
refactor: Extract calculateTotal() from processOrder()
refactor: Rename 'x' to 'customerCount' for clarity
refactor: Remove unused validateOldFormat() methodЗвіт про прогрес
Після кожної підфази звітувати користувачу:
- Внесені зміни
- Тести досі проходять?
- Виявлені проблеми
- Запитати: «Продовжити з наступною порцією?»
Фаза 6: Перегляд та ітерація
Контрольний список після рефакторингу
- Усі тести проходять
- Немає нових попереджень/помилок
- Код успішно компілюється
- Поведінка не змінилася (ручна верифікація)
- Документація оновлена за потреби
- Історія комітів чиста
Порівняння метрик
Запустити аналіз складності до і після:
python scripts/analyze-complexity.py <file>Представити покращення:
- Зміна кількості рядків коду
- Зміна цикломатичної складності
- Зміна індексу супровідності
Перегляд користувачем
Представити фінальні результати:
- Резюме всіх змін
- Порівняння коду до/після
- Покращення метрик
- Залишковий технічний борг
- Запитати: «Чи задоволені ви цими змінами?»
Наступні кроки
Обговорити з користувачем:
- Додаткові запахи для усунення?
- Запланувати подальший рефакторинг?
- Застосувати аналогічні зміни в інших місцях?
Важливі рекомендації
Коли ЗУПИНИТИСЯ та запитати
Завжди паузу та консультацію з користувачем, коли:
- Невпевненість щодо бізнес-логіки
- Зміна може вплинути на зовнішні API
- Покриття тестами недостатнє
- Потрібне значне архітектурне рішення
- Рівень ризику зростає
- Зустрічаєте неочікувану складність
Правила безпеки
- Ніколи не рефакторити без тестів (якщо користувач явно не підтвердив ризик)
- Ніколи не робити великих змін — розбивати на крихітні кроки
- Ніколи не пропускати запуск тестів після кожної зміни
- Ніколи не продовжувати, якщо тести не проходять — виправити або відкатити
- Ніколи не припускати — якщо сумніваєтесь, запитайте
Чого НЕ робити
- Не поєднуйте рефакторинг з додаванням функцій
- Не рефакторте під час аварій на продакшні
- Не рефакторте код, який не розумієте
- Не переускладнюйте — тримайте просто
- Не рефакторте все одразу
Приклад швидкого старту
Сценарій: Довгий метод з дублюванням
До:
function processOrder(order) {
// 150 рядків коду з:
// - Дубльованою логікою валідації
// - Інлайн-обчисленнями
// - Змішаними відповідальностями
}Кроки рефакторингу:
- Переконатися, що тести існують для processOrder()
- Витягти валідацію у validateOrder()
- Тест — має пройти
- Витягти обчислення у calculateOrderTotal()
- Тест — має пройти
- Витягти сповіщення у notifyCustomer()
- Тест — має пройти
- Перегляд — processOrder() тепер оркеструє 3 чіткі функції
Після:
function processOrder(order) {
validateOrder(order);
const total = calculateOrderTotal(order);
notifyCustomer(order, total);
return { order, total };
}Довідники
- Каталог запахів коду — повний список запахів коду
- Каталог рефакторингів — техніки рефакторингу
- Шаблон плану рефакторингу — шаблон планування
Скрипти
scripts/analyze-complexity.py— аналіз метрик складності кодуscripts/detect-smells.py— автоматичне виявлення запахів
Історія версій
- v1.0.0 (2025-01-15): Початковий випуск з методологією Фаулера, поетапним підходом, точками консультації з користувачем

