← Назад к вопросам

Есть ли ограничение у рефакторинга?

1.7 Middle🔥 101 комментариев
#Архитектура и паттерны

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Ограничения рефакторинга

Да, рефакторинг имеет явные ограничения и границы. Это очень важный момент, который многие junior разработчики упускают.

Основное ограничение: Behavior не должен меняться

Главное правило рефакторинга — это не добавление функционала.

# ДО рефакторинга
def calculate_total(items):
    total = 0
    for item in items:
        total += item['price'] * item['quantity']
        if item['quantity'] > 10:
            total *= 0.9  # 10% скидка
    return total

# ✅ Правильный рефакторинг (функционал сохранен)
def calculate_total(items):
    return sum(
        item['price'] * item['quantity'] * (0.9 if item['quantity'] > 10 else 1.0)
        for item in items
    )

# ❌ НЕПРАВИЛЬНО! Добавил новый функционал
def calculate_total(items):
    return sum(
        item['price'] * item['quantity'] * (0.9 if item['quantity'] > 10 else 1.0)
        for item in items
        if item['available']  # <-- НОВАЯ фильтрация! Это не рефакторинг
    )

Ограничение 1: Тесты должны оставаться зелеными

Если тесты падают после рефакторинга — это признак, что ты изменил behavior.

# Исходный код с тестом
def get_user_name(user_id):
    user = db.query(User).filter(User.id == user_id).first()
    return user.first_name + " " + user.last_name if user else None

# Тест
def test_get_user_name():
    assert get_user_name(1) == "John Doe"
    assert get_user_name(999) is None

# Рефакторинг (улучшение кода, но тесты все еще проходят)
def get_user_name(user_id):
    user = db.query(User).get(user_id)
    return f"{user.first_name} {user.last_name}" if user else None

# Тесты все еще проходят ✅

Если после рефакторинга тесты падают — это не рефакторинг, это изменение функционала.

Ограничение 2: Тесты должны писаться ДО рефакторинга

Без тестов рефакторинг опасен. Даже лучший разработчик может сломать неочевидное поведение.

# Если нет тестов, не рефакторю
legacy_function = """
Очень сложная функция, которую никто не понимает.
Нет тестов. Может делать что-то неочевидное.
"""

# Сначала пишу тесты
def test_legacy_function():
    assert legacy_function(1, 2) == expected_output_1
    assert legacy_function(0, 0) == expected_output_2
    # ... и так далее

# ЗАТЕМ рефакторю (уверен, что поведение не изменится)

Ограничение 3: Performance критичный код требует осторожности

У performance-sensitive кода есть строгие требования. Рефакторинг не должен замедлить код.

# ДО (быстро)
def find_duplicates(items):
    seen = set()
    duplicates = set()
    for item in items:
        if item in seen:
            duplicates.add(item)
        else:
            seen.add(item)
    return duplicates

# ❌ ОШИБКА! Это медленнее (O(n^2) вместо O(n))
def find_duplicates(items):
    return {item for item in items if items.count(item) > 1}

# ✅ Правильно (O(n), как и было)
def find_duplicates(items):
    seen = set()
    duplicates = set()
    for item in items:
        if item in seen:
            duplicates.add(item)
        else:
            seen.add(item)
    return duplicates

Когда код work на 100K записей за 1 сек, и ты его "улучшил" до 10 сек — это не рефакторинг.

Ограничение 4: Интерфейс должен оставаться прежним

Даже если ты улучшил внутреннюю реализацию, сигнатура функции не должна меняться (в public API).

# Исходная функция (public API)
def calculate_price(quantity: int, unit_price: float) -> float:
    return quantity * unit_price * 1.2  # с налогом

# ❌ НЕПРАВИЛЬНО! Изменил сигнатуру
def calculate_price(quantity: int, unit_price: float, include_tax: bool = True) -> float:
    base = quantity * unit_price
    return base * 1.2 if include_tax else base

# Все вызывающие коды сломались!
# client.calculate_price(10, 50)  # Теперь считает tax по умолчанию

# ✅ ПРАВИЛЬНО! Сигнатура та же, внутренняя реализация лучше
def calculate_price(quantity: int, unit_price: float) -> float:
    return _apply_tax(quantity * unit_price)

def _apply_tax(amount: float) -> float:
    return amount * 1.2

Ограничение 5: Рефакторинг имеет экономическую стоимость

Время на рефакторинг — это время, не потраченное на новые фичи. Иногда лучше оставить код как есть.

# Есть функция, которую используют 2 человека
# Она немного запутанная, но работает
# Улучшение сложности: на 8 часов для 2% ускорения
# РЕШЕНИЕ: не рефакторим, ниже ROI

# Есть функция, которую вызывают 1000 раз в день
# Она медленная
# Улучшение сложности: на 16 часов для 30% ускорения
# РЕШЕНИЕ: рефакторим! Экономия месячная: 100+ часов серверного времени

Ограничение 6: Не рефакторишь код, который не понимаешь

Это опасно. Даже если код выглядит странно, там может быть логика.

# Я вижу:
def process_order(order):
    if order.total < 100 and order.country == 'US':
        order.total *= 1.05
    return save(order)

# Код выглядит странно (зачем +5% для US?)
# Но я не знаю историю, не знаю требования
# БЕЗ ТЕСТОВ я не трогаю

# С ТЕСТАМИ я могу понять, что это делает
def test_process_order():
    assert process_order(Order(total=50, country='US')).total == 52.5
    assert process_order(Order(total=50, country='GB')).total == 50

Практический пример: Когда нельзя рефакторить

Сценарий: Боевая система, которая работает в production, нет тестов.

def calculate_damage(attacker, defender):
    damage = attacker.attack_power
    damage -= defender.defense
    damage *= get_random_multiplier()  # Может быть 0.8 или 1.2
    
    if attacker.has_buff('fire'):
        damage *= 1.5
    
    # Какая-то магия, которую никто не помнит
    if defender.has_buff('protection') and random() < 0.5:
        damage *= 0.5
    
    return max(1, int(damage))

# НЕЛЬЗЯ РЕФАКТОРИТЬ БЕЗ ТЕСТОВ!
# Это боевая система, малейшие изменения сломают игру

Правильный подход к рефакторингу

# 1. Пиши тесты, пока код не покрыт
for scenario in test_scenarios:
    assert original_function(**scenario) == expected[scenario]

# 2. Убедись, что тесты зелёные
runtime = pytest.main(['-v', test_file])
assert runtime == 0

# 3. Теперь можешь рефакторить
# Код меняется, тесты остаются

# 4. Все тесты все ещё зелёные?
runtime = pytest.main(['-v', test_file])
assert runtime == 0  # Да? Рефакторинг успешен

Резюме: Ограничения рефакторинга

  1. Behavior не меняется — функционал идентичен
  2. Тесты зелены — поведение подтверждается
  3. Performance не деградирует — скорость та же или лучше
  4. Интерфейс стабилен — сигнатуры функций не меняются
  5. Экономический смысл — ROI положителен
  6. Понимание кода — не рефакторишь, что не понимаешь
  7. Нет срочности — рефакторинг не блокирует features

Рефакторинг — это про улучшение кода, а не про его переделку. Если ты переделываешь — это уже новая разработка.

Есть ли ограничение у рефакторинга? | PrepBro