Clean Code на JavaScript: принципы и примеры на практике

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Clean Code на JavaScript: принципы и примеры на практике

Чистый код (Clean Code) — это не просто модный термин, а философия разработки, которая превращает хаотичный набор инструкций в стройную, читаемую и поддерживаемую систему. В мире JavaScript и Node.js, где проекты быстро растут и усложняются, умение писать чистый код становится критически важным навыком. В этой статье мы разберем ключевые принципы Clean Code на конкретных примерах с использованием Node.js.

Почему чистый код важен для JavaScript-разработчика?

JavaScript — язык с динамической типизацией и высокой гибкостью, что одновременно является его сильной стороной и источником проблем. Без соблюдения дисциплин чистого кода, проект быстро превращается в "болото", где любое изменение может сломать неожиданные части системы. Clean Code помогает:

  • Уменьшить количество багов и время на их поиск.
  • Ускорить онбординг новых разработчиков в проект.
  • Облегчить рефакторинг и масштабирование кодовой базы.
  • Повысить качество code review.

Принцип 1: Осмысленные имена — основа понимания

Первый и самый важный принцип Clean Code — правильное именование переменных, функций, классов и файлов. Имя должно отвечать на вопросы "что это?" и "зачем это?". Избегайте однобуквенных переменных (кроме счетчиков циклов) и абстрактных названий.

Плохой пример (Node.js):

// Что делает эта функция? Непонятно.
function d(a, b) {
  let r = [];
  for (let i = 0; i < a.length; i++) {
    if (b.includes(a[i])) {
      r.push(a[i]);
    }
  }
  return r;
}

// Использование
const arr1 = [1, 2, 3, 4, 5];
const arr2 = [3, 4, 5, 6, 7];
const result = d(arr1, arr2);
console.log(result); // [3, 4, 5]

Хороший пример (Clean Code):

// Имя функции и параметров говорят сами за себя
function findCommonElements(array1, array2) {
  const commonElements = [];
  for (const element of array1) {
    if (array2.includes(element)) {
      commonElements.push(element);
    }
  }
  return commonElements;
}

// Использование
const firstList = [1, 2, 3, 4, 5];
const secondList = [3, 4, 5, 6, 7];
const intersection = findCommonElements(firstList, secondList);
console.log(intersection); // [3, 4, 5]

Дополнительные рекомендации по именованию:

  • Булевы переменные: используйте префиксы is, has, can (например, isActive, hasPermission).
  • Функции: используйте глаголы (getUserData, validateEmail, sendNotification).
  • Константы: пишите в UPPER_SNAKE_CASE (MAX_RETRY_COUNT, DEFAULT_TIMEOUT_MS).

Принцип 2: Маленькие функции с одной ответственностью

Каждая функция должна делать только одну вещь и делать её хорошо. Если функция выполняет несколько действий (например, валидирует данные, делает запрос к БД и отправляет email), её нужно разбить на несколько маленьких функций. Это улучшает тестируемость и переиспользование кода.

Плохой пример (Node.js Express):

app.post('/api/users', async (req, res) => {
  const { name, email, password } = req.body;

  // Валидация
  if (!name || !email || !password) {
    return res.status(400).json({ error: 'Missing fields' });
  }
  if (password.length < 8) {
    return res.status(400).json({ error: 'Password too short' });
  }

  // Работа с БД
  const existingUser = await db.findUserByEmail(email);
  if (existingUser) {
    return res.status(409).json({ error: 'User exists' });
  }

  const hashedPassword = await bcrypt.hash(password, 10);
  const newUser = await db.createUser({ name, email, password: hashedPassword });

  // Логирование и ответ
  logger.info(`User created: ${newUser.id}`);
  res.status(201).json({ id: newUser.id, name: newUser.name });
});

Хороший пример (Clean Code с разделением ответственности):

// Функция валидации
function validateUserInput(name, email, password) {
  if (!name || !email || !password) {
    throw new Error('Missing required fields: name, email, password');
  }
  if (password.length < 8) {
    throw new Error('Password must be at least 8 characters');
  }
}

// Функция работы с БД
async function createUserInDatabase(name, email, password) {
  const existingUser = await db.findUserByEmail(email);
  if (existingUser) {
    throw new Error('User with this email already exists');
  }
  const hashedPassword = await bcrypt.hash(password, 10);
  return db.createUser({ name, email, password: hashedPassword });
}

// Основной обработчик (только оркестрация)
app.post('/api/users', async (req, res) => {
  try {
    const { name, email, password } = req.body;
    validateUserInput(name, email, password);
    const newUser = await createUserInDatabase(name, email, password);
    logger.info(`User created: ${newUser.id}`);
    res.status(201).json({ id: newUser.id, name: newUser.name });
  } catch (error) {
    if (error.message.includes('Missing') || error.message.includes('Password')) {
      return res.status(400).json({ error: error.message });
    }
    if (error.message.includes('already exists')) {
      return res.status(409).json({ error: error.message });
    }
    logger.error('Unexpected error creating user:', error);
    res.status(500).json({ error: 'Internal server error' });
  }
});

Принцип 3: Избегайте глубокой вложенности и используйте Guard Clauses

Глубокая вложенность (if внутри if внутри for) делает код нечитаемым. Используйте Guard Clauses (защитные условия) для раннего возврата из функции при ошибках или исключительных ситуациях. Это выравнивает основной поток выполнения и делает его линейным.

Плохой пример (глубокая вложенность):

function processOrder(order) {
  if (order) {
    if (order.isPaid) {
      if (order.items && order.items.length > 0) {
        // ... 50 строк логики обработки
        return                

Блоги

Рекомендаций по книгам