Рефакторинг6 мин чтения2026-03-06

Рефакторинг JavaScript: как переписывать код и не сойти с ума

Знаете это чувство, когда открываешь файл, который писал год назад? Сначала непонимание. Потом стыд. Потом хочется всё удалить и написать заново. Знакомо? Это н

Знаете это чувство, когда открываешь файл, который писал год назад? Сначала непонимание. Потом стыд. Потом хочется всё удалить и написать заново. Знакомо? Это нормально. Код устаревает, требования меняются, а мы учимся.

Рефакторинг javascript — это не про переписывание ради переписывания. Это про то, чтобы код работал лучше, читался легче, а баги находились до того, как их увидят пользователи. Давайте разберёмся, как это делать так, чтобы не создать новых проблем.

Рефакторинг кода — это простыми словами

Если совсем просто: вы меняете внутреннее устройство кода, не меняя его поведение. Как переставить мебель в комнате — стены те же, но жить стало удобнее.

Функция, которая раньше вычисляла скидку в 20 строк, теперь делает то же самое в 5. Багов не прибавилось, тесты проходят. Но читать её стало можно без аспирина.

На одном проекте мы унаследовали легаси-монстра. 800 строк в одном файле, функции по 200 строк, копипаст везде. Первые две недели я боялся трогать вообще что-либо. Потом понял: если не рефакторить, развитие остановится совсем. Начали с малого — выделили утилиты, покрыли тестами критичные места. Через полгода код был уже не страшно открывать.

Ключевое отличие рефакторинга от переписывания — маленькие шаги. Вы не сносите дом, вы меняете окна по одному. Каждый шаг — рабочий код. Это важно.

Когда код кричит о помощи: code smells

Прежде чем хвататься за клавиатуру, надо понять, что именно не так. Есть классические признаки — их называют code smells, «запахи кода». Не баги, но симптомы проблем.

Длинные функции. Если функция не помещается на один экран — что-то не так. Она делает слишком много. Разбивайте.

// До: функция на 80 строк, делает всё сразу
function processOrder(order) {
  // валидация (15 строк)
  // расчёт скидки (20 строк)
  // работа с базой (15 строк)
  // отправка email (10 строк)
  // логирование (20 строк)
}

// После: каждая ответственность — своя функция
function processOrder(order) {
  validateOrder(order);
  const total = calculateTotal(order);
  saveOrder(order, total);
  sendConfirmation(order);
  logOrder(order);
}

Дублирование. Скопировали кусок кода? Вы создали будущий баг. Измените одно место — забудете про второе. На моём опыте 80% багов в легаси — это когда поправили тут, а там забыли.

// До: скидка считается в трёх местах по-разному
function getRegularPrice(item) {
  return item.price * 0.9; // скидка 10%
}

function getVipPrice(item) {
  return item.price * 0.85; // скидка 15%
}

function getPromoPrice(item) {
  return item.price * 0.8; // скидка 20%
}

// После: одна функция, понятная логика
function getPrice(item, discountRate) {
  return item.price * (1 - discountRate);
}

Магические числа. Что такое 86400? Правильно, секунды в сутках. Но через месяц вы это забудете.

// До
if (user.lastLogin > Date.now() - 86400000) {
  // ...
}

// После
const DAY_IN_MS = 24 * 60 * 60 * 1000;
if (user.lastLogin > Date.now() - DAY_IN_MS) {
  // ...
}

Глубокая вложенность. Четыре уровня if — это уже сложно. Пять — мозг взрывается.

// До: пирамида смерти
function processUser(user) {
  if (user) {
    if (user.isActive) {
      if (user.hasPermission) {
        if (user.subscription === 'premium') {
          return grantAccess(user);
        }
      }
    }
  }
  return null;
}

// После: ранний выход
function processUser(user) {
  if (!user) return null;
  if (!user.isActive) return null;
  if (!user.hasPermission) return null;
  if (user.subscription !== 'premium') return null;
  
  return grantAccess(user);
}

Честно? Большинство команд живут с этими проблемами годами. Код работает, бизнес не жалуется. Но цена — замедление разработки. Новые фичи внедряются всё медленнее, баги находятся всё труднее.

Методы рефакторинга: что реально работает

Техник много. Назову те, которые использую чаще всего.

Извлечение функции. Самый частый приём. Видите кусок кода с комментарием? Это готовая функция. Комментарий станет названием.

// До: комментарий объясняет, что делает код
function renderPage(data) {
  // фильтруем неактивные элементы
  const active = data.filter(item => item.status === 'active');
  
  // сортируем по дате
  active.sort((a, b) => b.createdAt - a.createdAt);
  
  // показываем только первые 10
  return active.slice(0, 10);
}

// После: код сам объясняет себя
function renderPage(data) {
  const recentActiveItems = getRecentActiveItems(data);
  return recentActiveItems;
}

function getRecentActiveItems(data) {
  return data
    .filter(item => item.status === 'active')
    .sort((a, b) => b.createdAt - a.createdAt)
    .slice(0, 10);
}

Переименование. Звучит тривиально. Но правильное имя переменной или функции — это полдела. data — плохо. activeUsers — хорошо. x — ужасно. currentUserIndex — отлично.

Замена условного оператора полиморфизмом. Если switch или куча if проверяют тип объекта — это кандидат на полиморфизм.

// До: switch растёт с каждым новым типом
function getShapeArea(shape) {
  switch (shape.type) {
    case 'circle':
      return Math.PI * shape.radius ** 2;
    case 'rectangle':
      return shape.width * shape.height;
    case 'triangle':
      return 0.5 * shape.base * shape.height;
    default:
      return 0;
  }
}

// После: каждый объект сам знает свою площадь
const shapes = {
  circle: (r) => Math.PI * r ** 2,
  rectangle: (w, h) => w * h,
  triangle: (b, h) => 0.5 * b * h,
};

function getShapeArea(shape) {
  const calculator = shapes[shape.type];
  return calculator ? calculator(shape) : 0;
}

Ну вот смотрите — стало чище, да? И добавлять новые типы теперь проще.

Как не сломать всё при рефакторинге

Главная ошибка — рефакторить без тестов. Это как менять двигатель на ходу. Может получится. Скорее нет.

План такой:

Во-первых, тесты. Хотя бы на критичный функционал. Если тестов нет — напишите их перед рефакторингом. Да, это скучно. Да, это обязательно.

// Пример теста перед рефакторингом
describe('calculateDiscount', () => {
  it('returns 10% for regular users', () => {
    expect(calculateDiscount('regular')).toBe(0.1);
  });
  
  it('returns 20% for VIP users', () => {
    expect(calculateDiscount('vip')).toBe(0.2);
  });
});

Во-вторых, маленькие изменения. Один тип рефакторинга за раз. Переименовали — закоммитили. Вынесли функцию — закоммитили. Каждое изменение должно быть атомарным.

В-третьих, не смешивать. Рефакторинг отдельно, новые фичи отдельно. Если начали рефакторить — не добавляйте новый функционал. Это соблазн, который ведёт к боли.

Короче, правило простое: после каждого шага код должен работать. Если сломалось — вы сделали слишком большой шаг.

Инструменты помогают. ESLint найдет неиспользуемые переменные. Prettier приведёт код к единому стилю. Но главное — автоматический code review. Я сейчас использую Distiq для этих задач — бот проверяет каждый MR и подсвечивает проблемы до того, как они попадут в основную ветку. Экономит время на ревью и ловит типичные ошибки.

Что в итоге

Рефакторинг кода javascript — это не разовая акция. Это привычка. Регулярная гигиена, как чистка зубов. Пропустили пару раз — запахло. Запустили — уже больно.

Начните с малого. Самый неприятный файл. Самая запутанная функция. Один шаг за раз. Через месяц код станет другим. Через полгода — вы его узнаете.

А если хотите, чтобы кто-то подсказывал, что именно стоит поправить — попробуйте Distiq. Это российский сервис AI code review, который интегрируется в GitLab, GitHub и GitVerse. Бот смотрит каждый MR и оставляет комментарии — где дублирование, где можно упростить, где потенциальный баг. Бесплатно работает с открытыми репозиториями, для приватных есть тарифы. Настраивается за пару минут, а польза — постоянная.

Попробуйте Distiq для автоматического code review

AI-бот анализирует каждый MR/PR и оставляет комментарии с замечаниями. Интеграция за 2 минуты.

Попробовать бесплатно

Похожие статьи