Перейти к основному содержимому

Декларативный код — что и как

Разработчику

Декларативный стиль описывает желаемый результат; императивный — пошаговые команды «как добиться». Функциональный язык для этого не обязателен: цель — чтобы читатель видел намерение без разбора циклов и счётчиков. Связь с MAPPER: код читается как псевдокод предметной области.

См. также императивный стиль, функциональное программирование, async.


«Что» и «как»

Императивно (как)Декларативнее (что)
for + if + pushfilter / map / LINQ / comprehensions
if (a && !b || c) без имениif (isEligibleForDiscount(order))
Ручной разбор JSON поле за полемdeserialize в тип + валидация схемы
Вложенные callbackasync/await, Promise.all, pipeline

Один уровень абстракции в функции: либо бизнес-правило, либо деталь I/O — не оба сразу (связанность).


Имена вместо логики в лоб

  • Магические числа → именованные константы или enum (MaxRetries, VatRate).
  • Дурацкие имена методов (doStuff, processData) → глагол + объект домена (IssueInvoice).
  • Двойное отрицание (if (!isNotValid)) → положительный предикат if (isValid).

Комментарий «увеличиваем i» в цикле — признак того, что цикл можно заменить итератором с именем (for (const line of lines)).


Итерации

Заменяйте явные циклы там, где язык даёт идиому:

// Императивно
const adults = [];
for (const u of users) {
if (u.age >= 18) adults.push(u);
}

// Декларативнее
const adults = users.filter((u) => u.age >= 18);
var total = orders.Where(o => o.IsPaid).Sum(o => o.Amount);

Цикл for остаётся уместным для горячих участков, индексов и низкоуровневого кода — но не для «пройти и отфильтровать» в доменном слое.


Скрытые предположения → явные

Вынесите допущения в именованные проверки или типы.

// Скрыто: координаты в градусах, заказ в копейках
function distance(lat1, lon1, lat2, lon2) { ... }

// Явно
function distance(a: GeoPoint, b: GeoPoint): Kilometers { ... }

См. примитивы и value objects.


Callback hell и асинхронность

Пирамида обратных вызовов — вложенные .then / callback без имён.

// Плохо: «как» на полэкрана
getUser(id, (err, user) => {
getOrders(user, (err, orders) => { ... });
});

// Читаемее: последовательность намерений
const user = await getUser(id);
const orders = await getOrders(user);

Для параллельных запросов — Promise.all / Task.WhenAll с явным списком задач.


Сообщения об ошибках

Ошибка для человека — что случилось и что сделать, без внутреннего жаргона.

Неверный формат даты «31.02.2020». Укажите дату как ДД.ММ.ГГГГ.

«Магические исправления» (return 0, catch {}) маскируют дефект; лучше быстрый провал с ясным исключением или Result.


Регулярные выражения

Regex без имени — нечитаем. Вынесите в константу с комментарием-спецификацией или в класс EmailPattern / используйте парсер библиотеки. Для списка допустимых значений — enum или таблица, а не «универсальный» regex.


Условия Йоды

Переписывайте условия в форме «утверждение о мире», без отрицаний и лишних скобок. Связь с условиями и null (меньше if).


Версионированные методы

calculateV1, calculateV2 в одном классе — путаница. Оставьте одну рабочую версию; старую удалите или спрячьте за feature flag с датой снятия (YAGNI).


Пустые строки и область видимости

  • Узкая область для переменных; не переиспользуйте result для разных смыслов.
  • Лишние пустые строки внутри функции не добавляют смысл; группируйте логические блоки методами.

Декларативность vs производительность

Выбирайте читаемость, пока профилировщик не показал узкое место (преждевременная оптимизация). LINQ, лишние аллокации в hot path — оптимизируют после измерения.


Краткий чек-лист

ТемаДействие
Область видимостиКороткая жизнь переменных
Магические числаКонстанты, enum
Итерацииmap / filter / LINQ в доменном слое
Asyncasync/await, Promise.all
ОшибкиПонятное сообщение, без пустого catch
RegexИменованный паттерн или тип

Подробнее по симптомам — справочник тем.


Куда дальше


См. также

Другие статьи этого же раздела в боковом меню (как на странице "О разделе").