Именование в проекте: как выбирать названия, которые не ломаются
Плохое имя превращает простой код в расследование: разработчик ищет, что скрывается за data, manager или handle. Разбираем рабочие приёмы именования для TypeScript 5.x и правила, которые помогают команде быстрее читать и менять код.

Переменная с именем data не объясняет, что в ней лежит, а функция handle скрывает причину и результат действия. В TypeScript-проекте такие названия редко ломают сборку, но увеличивают время ревью, провоцируют дублирование логики и делают безопасный рефакторинг дороже. Ниже — практики для TypeScript 5.x: как называть сущности по смыслу, отличать входные данные от результата и не превращать имена в длинные комментарии.
Начните с границы ответственности
Имя должно отвечать на вопрос, который возникнет у следующего читателя кода. Для функции это обычно «что она делает и с чем», для переменной — «какое значение здесь хранится», для класса или модуля — «за какую часть предметной области он отвечает».
Первый рабочий приём — записать роль сущности одним коротким предложением, а затем убрать из имени слова, которые ничего не добавляют. Например, функция «получает активные заказы пользователя из базы» не должна называться getData. В слое репозитория уместнее findActiveOrdersByUserId, если функция действительно ищет данные по идентификатору пользователя. В сервисе, который уже скрывает детали хранения, лучше getActiveOrders: слово database там раскрывает лишнюю реализацию.
- Сущность:
order,customer,invoice. - Коллекция: существительное во множественном числе —
orders,customers. - Предикат: вопрос или проверка —
isArchived,hasPermission,canRetry. - Действие: глагол с объектом —
cancelOrder,sendInvoice,calculateTax. - Результат: имя описывает значение, а не процесс его получения —
totalAmount, а неcalculateTotal.
Сноска. Предикат — выражение, которое возвращает логическое значение: true или false. Такие имена помогают читать условие как фразу: if (canRetry) проще расшифровать, чем if (retry).
Три подхода, которые работают в TypeScript
1. Называть по предметной области
Если код работает с заказами, платежами и возвратами, эти слова должны появляться в именах. refund точнее, чем operation, а paymentStatus точнее, чем status. Контекст файла помогает, но не всегда спасает: переменная быстро попадёт в лог, тест или вызов другой функции.
Не смешивайте термины. Если в бизнес-правилах используется слово «заказ», не называйте тот же объект purchase в одном модуле и order в другом без причины. Разные слова могут означать разные сущности, а могут быть случайными синонимами. Для команды это лишняя проверка при каждом изменении.
2. Называть по действию и результату
У функции должен быть видимый эффект или результат. Сравните processPayment и authorizePayment. Первое может означать что угодно: проверку карты, списание или запись события. Второе задаёт более узкий контракт, если функция действительно только авторизует платёж.
Слова handle, process, manage, do и utils допустимы только на границе, где смысл раскрывается сразу. Обработчик события может называться handleOrderCreated, но функция внутри него должна иметь конкретное имя: reserveInventory или sendOrderConfirmation.
3. Называть по состоянию, а не по текущему месту
Имена вроде result, response и current часто понятны только в пяти соседних строках. После перемещения кода они теряют смысл. Лучше описать состояние и источник: validatedOrder, apiResponse, selectedPlan.
При этом не добавляйте тип в имя без необходимости. userList хуже, чем users, если тип уже показывает массив. orderMap может быть полезным именем, когда структура действительно важна для алгоритма и отличается от обычного списка.
| Подход | Когда применять | Цена | Пример |
|---|---|---|---|
| Предметный термин | Сущности и поля домена | Нужно согласовать словарь команды | shipmentAddress |
| Действие и объект | Функции с понятным эффектом | Имя может стать длиннее | cancelSubscription |
| Состояние и источник | Промежуточные значения и результаты | Нужно отличать похожие состояния | validatedPayload |
| Тип в имени | Только когда структура важна для чтения | Риск дублировать TypeScript-тип | ordersById |
Хорошее имя не объясняет всю реализацию. Оно убирает главный вопрос читателя: что это за значение и какую роль оно играет здесь.
Как писать имена, которые переживают рефакторинг
Для TypeScript 5.x удобно держать несколько правил на уровне проекта. Это не универсальный стандарт: важнее последовательность внутри репозитория и совпадение с терминологией продукта.
- Сначала выберите язык терминов. Если кодовая база англоязычная, не переводите отдельные сущности на русский и не смешивайте
customerсclientбез договорённости. Проверка: один и тот же объект называется одинаково в типе, сервисе, тесте и документации. - Разделяйте вход и выход. Для данных запроса используйте
createOrderInput, для результата —createdOrder. Проверка: по имени можно понять, можно ли изменять объект и на каком этапе он находится. - Выносите отрицание в имя предиката. Предпочтительнее
isExpired, чемnotActive, если речь идёт о конкретном состоянии. Проверка: условие не требует двойного отрицания вродеif (!isNotReady). - Не кодируйте временную реализацию. Если сегодня данные приходят из PostgreSQL, а завтра из HTTP-сервиса, имя
postgresOrderRepositoryв бизнес-слое станет препятствием. Проверка: замена источника данных не требует массового переименования доменных сущностей. - Называйте обработчики по событию.
onSubmitподходит для UI-компонента, но в бизнес-логике лучшеonInvoicePaidилиhandleInvoicePaid. Проверка: по имени понятен триггер, а не только технический механизм вызова.
В TypeScript это особенно важно из-за структурной типизации: два объекта могут быть совместимы по полям, хотя относятся к разным этапам процесса. Имя не заменяет отдельные типы, но помогает заметить ошибку на ревью.
type CreateOrderInput = {
customerId: string;
productIds: string[];
};
type Order = {
id: string;
customerId: string;
productIds: string[];
status: 'new' | 'paid' | 'cancelled';
};
function createOrder(input: CreateOrderInput): Order {
const newOrder: Order = {
id: crypto.randomUUID(),
customerId: input.customerId,
productIds: input.productIds,
status: 'new'
};
return newOrder;
}
В этом фрагменте имя input оправдано только внутри функции: его роль очевидна из сигнатуры. Имя newOrder показывает результат конкретного этапа. Если функция начнёт возвращать не созданный заказ, а идентификатор или команду на сохранение, название станет сигналом для пересмотра контракта.
Когда выбирать короткое имя, а когда уточнять
Короткое имя хорошо работает в маленьком локальном блоке: index в цикле или date рядом с одной операцией. Чем дольше живёт значение и чем больше у него потребителей, тем больше контекста должно быть в имени.
- Оставляйте короткое имя, если переменная используется в пределах нескольких строк и не пересекает границу функции.
- Добавляйте уточнение, если рядом есть два значения одного типа:
billingAddressиshippingAddress, а не два разаaddress. - Убирайте дублирование контекста, если имя модуля уже отвечает на вопрос. В файле
payment-serviceпеременнаяpaymentPaymentStatusперегружена. - Используйте суффикс
ById,At,CountилиUrl, когда он меняет смысл:ordersById,createdAt,retryCount.
Что ломается на практике
Синонимы одного понятия
user, account и customer могут быть тремя разными сущностями, а могут обозначать одного пользователя. Если граница не определена, согласуйте словарь до переименования. Массовая замена слов без понимания модели только маскирует проблему.
Имена, обещающие больше, чем делает код
Функция validateOrder, которая лишь проверяет наличие обязательных полей, не должна выглядеть как проверка доступности товара и права на покупку. Уточните имя до validateOrderFields или разделите проверки. Иначе вызывающий код начнёт полагаться на несуществующую гарантию.
Аббревиатуры и контекстные сокращения
cfg, ctx, req и res привычны внутри короткого HTTP-обработчика, но хуже подходят для публичных функций и бизнес-логики. Сокращение экономит несколько символов, а расшифровка может потребовать чтения документации. Оставляйте аббревиатуры только там, где команда использует их одинаково и смысл очевиден.
Ложная точность
Слишком длинное имя тоже плохо. orderThatWasLoadedFromTheExternalPaymentProvider переносит в переменную детали одного сценария. Если происхождение важно, разделите понятия: externalPaymentOrder для сущности и отдельный шаг преобразования для результата.
Что делать дальше
Выберите пять самых спорных терминов в текущем проекте и составьте короткий словарь: термин, значение, допустимые формы и запрещённые синонимы. Затем проверьте по нему имена публичных функций, типов и полей, а не каждую локальную переменную.
На следующем ревью ищите не только опечатки. Задавайте три вопроса: какой контракт обещает это имя, не смешивает ли оно вход с результатом и переживёт ли оно замену реализации. После этого добавьте в линтер только те ограничения именования, которые команда уже соблюдает вручную. Правило, которое постоянно отключают, создаёт шум, а не качество.
Защитник прав потребителей: Досудебные претензии на возврат товара со ссылками на ЗоЗПП.
Спросить за 15 ₽Источники: TypeScript Handbook: Everyday Types, typescript-eslint: naming-convention rule
Частые вопросы
Нужно ли всегда писать длинные и максимально подробные имена?
Стоит ли включать тип переменной в её имя?
Что делать, если в проекте уже смешаны user, customer и account?
Можно ли переименовывать публичные функции без риска?
Что делать, если хорошее имя уже занято?
Как проверить, что правила именования не ломают продакшен?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.