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

Как при рефакторинге кода понимаешь что нужно остановиться?

2.3 Middle🔥 203 комментариев
#Soft Skills и рабочие процессы

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

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

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

Когда остановиться при рефакторинге: чек-лист профессионала

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

1. Правило YAGNI - You Arent Gonna Need It

Первый сигнал к остановке - когда я начинаю писать код "на будущее":

// STOP! Это переженжиниринг
class UserService {
  async getUser(id) { /* ... */ }
  async getUsers(ids) { /* ... */ }
  async getUsersByRole(role) { /* ... */ }
  async getUsersByDepartment(dept) { /* ... */ }
  async getUsersByStatus(status) { /* ... */ }
  // И ещё 10 вариантов для всех комбинаций...
}

// ПРАВИЛЬНО - пишу только то, что нужно сейчас
class UserService {
  async getUser(id) { /* ... */ }
  async getUsersByIds(ids) { /* ... */ }
}

Сигнал к остановке: если нет issue/task, которая требует эту функцию, её быть не должно.

2. Тесты как якорь

Если тесты зелёные и покрытие достаточное - это знак того, что рефакторинг завершён:

// До рефакторинга
function calculatePrice(items, discount, tax, shipping) {
  let total = items.reduce((sum, item) => sum + item.price, 0);
  total = total * (1 - discount);
  total = total * (1 + tax);
  total = total + shipping;
  return Math.round(total * 100) / 100;
}

// После рефакторинга
function calculatePrice(items, discount, tax, shipping) {
  const subtotal = calculateSubtotal(items);
  const discounted = applyDiscount(subtotal, discount);
  const withTax = applyTax(discounted, tax);
  return addShipping(withTax, shipping);
}

// Когда остановиться?
// - Все исходные тесты проходят
// - Покрытие не упало
// - Новые функции поддерживаемы
// - Нет чувства "могу ещё улучшить"

Если все тесты проходят и покрытие хорошее - рефакторинг считаю завершённым.

3. Принцип "Достаточно хорошо"

Есть точка, после которой дополнительные улучшения дают минимальный ROI:

// Уровень 1: Работает (2 часа работы)
function filterAndMapUsers(users, role, needsActive) {
  return users.filter(u => u.role === role && u.active === needsActive)
    .map(u => ({ id: u.id, name: u.name }));
}

// Уровень 2: Более генеричное (4 часа работы)
function filterAndTransform(data, filters, mapper) {
  return data
    .filter(item => Object.entries(filters).every(
      ([key, value]) => item[key] === value
    ))
    .map(mapper);
}

// Уровень 3: Ultimate абстракция (8+ часов)
class QueryBuilder {
  // ...сложная логика...
}

// Где остановиться? На уровне 1-2
// Уровень 3 - это переженжиниринг для одной функции

Останавливаюсь, когда:

  • Код читаемый и понятный
  • Он решает текущую задачу
  • Нет pain points при использовании

4. Метрика: Cognitive Complexity

Использую простой чек - если функция требует больше 1-2 секунд на понимание, пора её упростить:

// Сложность 10/10 - STOP!
function process(data) {
  let result = [];
  for (let i = 0; i < data.length; i++) {
    if (data[i].type === "A") {
      if (data[i].status === "active") {
        if (data[i].priority > 5) {
          result.push({
            ...data[i],
            processed: true,
            timestamp: Date.now()
          });
        }
      }
    }
  }
  return result;
}

// Сложность 2/10 - ХОРОШО
function processHighPriorityItems(data) {
  return data
    .filter(item => item.type === "A" && item.status === "active")
    .filter(item => item.priority > 5)
    .map(item => ({ ...item, processed: true, timestamp: Date.now() }));
}

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

5. Правило Git Diff

Смотрю на размер изменений:

# Хороший рефакторинг - изменения понятны и локальны
git diff --stat
 app/services/userService.ts | 45 ++--
 
# Красная лампочка - изменяю слишком много файлов
git diff --stat
 app/services/userService.ts | 200 ++-
 app/hooks/useUser.ts | 150 +--
 app/components/UserCard.ts | 80 ++--
 app/types/index.ts | 60 +++-
 app/utils/helpers.ts | 120 +++

# STOP - это уже не рефакторинг, а переписывание

Если рефакторинг затрагивает 5+ файлов - пора остановиться или разбить на части.

6. Performance Baseline

Запускаю lighthouse/profiler до и после:

// Измеряю
console.time("render");
ReactDOM.render(<App />, root);
console.timeEnd("render");

// Если улучшение < 5% и код усложнился - STOP
// Если улучшение > 20% и код проще - продолжаем

7. Code Review Feedback Loop

Когда я сам себя начинаю критиковать:

// Мой внутренний монолог:
// "А может вместо этого паттерна использовать..."
// "Хм, это было бы лучше, если б я..."
// "Да, но может быть..."

// STOP! Это признак того, что я overthinking

Когда появляется рефлексия "может быть" - это точка для остановки и созыва code review.

Чек-лист остановки

  1. Тесты проходят и покрытие > 80%
  2. Код занимает <= 30 строк на функцию
  3. Cognitive complexity в норме (читаемо за 5 секунд)
  4. Изменил <= 3 файлов
  5. Performance не ухудшилась
  6. Ещё 2 идеи по улучшению, но они требуют новых tasks
  7. Code review заметил только minor issues

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

// Было: хардкод в компоненте
export function UserList() {
  return (
    <div className="space-y-4">
      {users.map(u => (
        <div key={u.id} className="p-4 bg-white rounded border">
          <h3>{u.name}</h3>
          <p>{u.email}</p>
        </div>
      ))}
    </div>
  );
}

// Шаг 1: Извлечь компонент
export function UserItem({ user }) {
  return (
    <div className="p-4 bg-white rounded border">
      <h3>{user.name}</h3>
      <p>{user.email}</p>
    </div>
  );
}

// Шаг 2: Добавить типизацию
interface UserItemProps {
  user: User;
  onSelect?: (user: User) => void;
}

// STOP! Здесь я остановился
// Дальше было бы:
// - Шаг 3: Кастомизируемые поля (YAGNI)
// - Шаг 4: Плагины для расширения (Оверенжиниринг)

Вывод

Останавливаюсь при рефакторинге, когда:

  1. Код работает правильно (тесты зелёные)
  2. Его легко читать (5-10 секунд на понимание)
  3. Следует SOLID, DRY, KISS
  4. ROI дальнейших улучшений минимален

Лучше иметь "хороший-достаточный" код, чем "идеальный-но-нечитаемый".