Комментарии и TODO

Комментарий — пояснение в коде, которое игнорируется интерпретатором. Хороший комментарий объясняет почему, а не что. Лучший код — самодокументирующийся.

Зачем нужно

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

Где используется

  • Пояснение нетривиальных решений
  • Документация публичных API (JSDoc)
  • Временные метки (TODO, FIXME, HACK)
  • Предупреждения и ограничения
  • Лицензионные заголовки

Предпосылки

Когда комментировать

Хорошие комментарии — объясняют ПОЧЕМУ

// Хорошо: объясняет бизнес-причину
// Скидка 20% для заказов старше 30 дней — требование маркетинга (#JIRA-1234)
if (daysSinceOrder > 30) {
  applyDiscount(order, 0.2);
}

// Хорошо: предупреждает о неочевидном поведении
// API возвращает даты в формате MM/DD/YYYY (американский формат!)
const date = parseUSDate(response.createdAt);

// Хорошо: объясняет "магический" код
// Битовый сдвиг на 0 — самый быстрый способ отбросить дробную часть
// (быстрее Math.floor для положительных чисел)
const intValue = floatValue | 0;

// Хорошо: ссылка на источник решения
// Алгоритм Фишера-Йетса для перемешивания массива
// Источник: https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
function shuffle(array) {
  for (let i = array.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random * (i + 1));
    [array[i], array[j]] = [array[j], array[i]];
  }
  return array;
}

Плохие комментарии — описывают ЧТО (и так видно из кода)

// ПЛОХО: комментарий повторяет код
// Увеличиваем счётчик на 1
counter++;

// Проверяем, является ли пользователь администратором
if (user.role === 'admin') { /* ... */ }

// Возвращаем список пользователей
return users;

// Создаём новый массив
const newArray = ;

// ПЛОХО: устаревший комментарий (код изменился, комментарий — нет)
// Отправляем email пользователю
sendPushNotification(user, message); // email давно заменён на push!

Самодокументирующийся код

Лучшая альтернатива комментариям — код, который не нуждается в пояснении.

// ПЛОХО: нужен комментарий, чтобы понять
// Проверяем, может ли пользователь голосовать
if (user.age >= 18 && user.citizen === true && !user.banned) {
  // ...
}

// ХОРОШО: код сам себя объясняет
function canVote(user) {
  const isAdult = user.age >= VOTING_AGE;
  const isCitizen = user.citizen === true;
  const isNotBanned = !user.banned;

  return isAdult && isCitizen && isNotBanned;
}

if (canVote(user)) {
  // ...
}
// ПЛОХО: нужен комментарий
// Получаем пользователей старше 18 лет с подпиской, отсортированных по имени
const result = users
  .filter(u => u.a >= 18 && u.s)
  .sort((a, b) => a.n.localeCompare(b.n));

// ХОРОШО: понятно без комментариев
const adultSubscribers = users
  .filter(user => user.age >= MINIMUM_AGE && user.hasSubscription)
  .sort((a, b) => a.name.localeCompare(b.name));

TODO-конвенция

TODO-метки маркируют технический долг и незавершённую работу.

Стандартные метки

// TODO: задача, которую нужно сделать
// TODO: добавить пагинацию когда пользователей > 1000

// FIXME: известный баг, который нужно исправить
// FIXME: race condition при одновременных запросах

// HACK: временное некрасивое решение
// HACK: setTimeout(0) чтобы дождаться рендера DOM

// NOTE: важная информация для будущих разработчиков
// NOTE: этот endpoint вызывается и мобильным приложением, не меняйте контракт

// OPTIMIZE: место, которое можно оптимизировать
// OPTIMIZE: N+1 запрос к БД, нужен JOIN

// DEPRECATED: код, который скоро будет удалён
// DEPRECATED: использовать newApi вместо этого метода

Формат TODO с контекстом

// TODO(username): краткое описание — JIRA-1234
// TODO(anton): реализовать кэширование для /api/products — PROJ-567
// FIXME(team): утечка памяти при долгом WebSocket соединении — BUG-890

Поиск TODO в проекте

# Найти все TODO в проекте
grep -rn "TODO\|FIXME\|HACK" src/

# VS Code: расширение "Todo Tree" подсветит все метки

JSDoc — документация API

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

/**
 * Вычисляет стоимость доставки заказа.
 *
 * @param {Object} order - Объект заказа
 * @param {number} order.total - Сумма заказа в рублях
 * @param {string} order.region - Регион доставки
 * @param {boolean} [order.express=false] - Экспресс-доставка
 * @returns {number} Стоимость доставки в рублях
 * @throws {Error} Если регион не поддерживается
 *
 * @example
 * calculateShipping({ total: 5000, region: 'moscow' });
 * // => 300
 *
 * calculateShipping({ total: 5000, region: 'moscow', express: true });
 * // => 600
 */
function calculateShipping(order) {
  const { total, region, express = false } = order;

  const rates = {
    moscow: 300,
    spb: 400,
    regions: 600,
  };

  const baseRate = rates[region];
  if (!baseRate) throw new Error(`Регион "${region}" не поддерживается`);

  const rate = express ? baseRate * 2 : baseRate;
  return total >= 10000 ? 0 : rate; // бесплатная доставка от 10000
}

/**
 * Пользователь системы.
 * @typedef {Object} User
 * @property {number} id - Уникальный идентификатор
 * @property {string} name - Имя пользователя
 * @property {string} email - Email
 * @property {'admin'|'user'|'moderator'} role - Роль
 * @property {Date} createdAt - Дата создания
 */

/**
 * Находит пользователя по ID.
 * @param {number} id - ID пользователя
 * @returns {Promise<User|null>} Пользователь или null
 */
async function findUserById(id) {
  return db.users.findOne({ id });
}

Основные JSDoc-теги

Тег Назначение Пример
@param Параметр функции @param {string} name
@returns Возвращаемое значение @returns {number}
@throws Исключения @throws {Error}
@example Пример использования Блок кода
@typedef Определение типа Описание объекта
@deprecated Устаревший код @deprecated Используйте newFn
@see Ссылка @see https://docs.example.com
@since Версия появления @since 2.0.0

Частые ошибки

  1. Комментировать очевидное. i++ // инкремент i — бесполезно
  2. Не обновлять комментарии. Устаревший комментарий хуже, чем его отсутствие
  3. Оставлять закомментированный код. Используйте git для истории, не комментарии
  4. Писать TODO без контекста. // TODO: fix this — fix что? когда? почему?
  5. Использовать комментарии вместо рефакторинга. Если нужен комментарий — может, стоит переписать код

Практика

  1. Просмотрите свой проект: удалите все закомментированные блоки кода
  2. Замените 3 комментария «что делает код» на самодокументирующийся код
  3. Добавьте JSDoc к 5 публичным функциям в проекте
  4. Установите расширение Todo Tree в VS Code и посмотрите все TODO

Связанные темы

Ресурсы

  • JSDoc — официальная документация (jsdoc.app)
  • Clean Code, глава 4 «Comments» (Robert C. Martin)
  • VS Code расширение: Todo Tree
  • VS Code расширение: Better Comments