Чистый код: правила именования и написания функций
Практика3 минРедакция Innovialabs
Коротко
Чистый код читается как текст: имена говорят, что хранится и что делается, функции короткие и делают одно дело, аргументов мало, а вложенность снимается ранним возвратом. Комментарии объясняют «почему», а не «что».
Главное правило
Код читают намного чаще, чем пишут. Поэтому цель — чтобы другой разработчик (или вы через полгода) понял фрагмент без лишних усилий. Ниже — правила, каждое с примером «до» и «после».
Имена раскрывают смысл
Имя должно отвечать на вопрос «что это и зачем», без расшифровки.
// До
const d = 30;
const arr = users.filter(u => u.a);
// После
const trialPeriodDays = 30;
const activeUsers = users.filter(user => user.isActive);
Практические правила:
- Переменные — существительные:
invoice,totalAmount. - Функции — глаголы:
calculateTotal,sendInvoice. - Булевы значения — вопрос:
isPaid,hasAccess,canEdit. - Без магических чисел: выносите их в именованные константы.
- Единый словарь: если в проекте
fetch, не смешивайте сget,loadиretrieveдля одного и того же. - Длина имени соразмерна области видимости:
iв цикле на три строки допустимо, в модуле — нет.
Функция делает одно дело
Если для описания функции нужно слово «и», скорее всего, её стоит разделить.
// До
function processOrder(order: Order) {
if (!order.items.length) throw new Error("Empty order");
let total = 0;
for (const item of order.items) total += item.price * item.qty;
db.save({ ...order, total });
mailer.send(order.email, `Сумма: ${total}`);
}
// После
function processOrder(order: Order) {
validateOrder(order);
const total = calculateTotal(order.items);
saveOrder(order, total);
notifyCustomer(order.email, total);
}
Теперь верхняя функция читается как оглавление, а каждую часть можно тестировать отдельно. Жёсткого лимита строк нет, но если функцию приходится прокручивать — это повод задуматься.
Мало аргументов
Чем больше параметров, тем легче перепутать порядок. Булевы флаги особенно коварны: по вызову не понять, что значит true.
// До
createUser("Ali", "ali@example.com", true, false, "ru");
// После
createUser({
name: "Ali",
email: "ali@example.com",
isAdmin: true,
sendWelcomeEmail: false,
locale: "ru",
});
Ориентир: до двух-трёх аргументов. Больше — передайте объект с именованными полями. Флаг, меняющий поведение функции, часто означает, что это две разные функции.
Ранний возврат вместо вложенности
Глубокие if заставляют держать в голове все условия сразу. Обрабатывайте особые случаи сразу и выходите.
// До
function getDiscount(user: User | null) {
if (user) {
if (user.isActive) {
if (user.orders > 10) {
return 0.1;
}
}
}
return 0;
}
// После
function getDiscount(user: User | null) {
if (!user || !user.isActive) return 0;
if (user.orders <= 10) return 0;
return 0.1;
}
Основной сценарий остаётся на верхнем уровне отступа, а «охранные» проверки стоят в начале.
Комментарии объясняют «почему»
Комментарий, пересказывающий код, устаревает и мешает. Полезный комментарий объясняет неочевидную причину.
// До
// увеличиваем счётчик на 1
retries++;
// После
// API провайдера иногда отвечает 503 при пиковой нагрузке,
// поэтому повторяем запрос, а не показываем ошибку сразу
retries++;
Если хочется написать комментарий «что делает этот блок», сначала попробуйте вынести блок в функцию с понятным именем.
Чек-лист перед коммитом
- Понятно ли по имени, что хранит переменная и что делает функция?
- Делает ли каждая функция одно дело?
- Есть ли функции с тремя и более аргументами или булевыми флагами?
- Можно ли убрать вложенность ранним возвратом?
- Не пересказывают ли комментарии код?
- Не осталось ли закомментированного кода? Для истории есть Git.