Декларативный код — что и как
Декларативный стиль описывает желаемый результат; императивный — пошаговые команды «как добиться». Функциональный язык для этого не обязателен: цель — чтобы читатель видел намерение без разбора циклов и счётчиков. Связь с MAPPER: код читается как псевдокод предметной области.
См. также императивный стиль, функциональное программирование, async.
«Что» и «как»
| Императивно (как) | Декларативнее (что) |
|---|---|
for + if + push | filter / map / LINQ / comprehensions |
if (a && !b || c) без имени | if (isEligibleForDiscount(order)) |
| Ручной разбор JSON поле за полем | deserialize в тип + валидация схемы |
| Вложенные callback | async/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 в доменном слое |
| Async | async/await, Promise.all |
| Ошибки | Понятное сообщение, без пустого catch |
| Regex | Именованный паттерн или тип |
Подробнее по симптомам — справочник тем.
Куда дальше
- Изменяемость — иммутабельность поддерживает декларативные цепочки данных.
- Однострочные приёмы — краткие идиомы, когда они понятны команде.
- Культура написания — стандарты и форматтер.
См. также
Другие статьи этого же раздела в боковом меню (как на странице "О разделе"). Именование, форматирование, комментарии, документация в коде и базовые принципы читаемости — практики, которые команда договаривается соблюдать каждый день. Цикломатическая сложность — одна из наиболее устойчиво применяемых метрик статического анализа программного кода, призванная количественно оценивать логическую структуру исполняемого модуля. Правило MAPPER (Model Abstract Partial Programmable Explaining Reality) — как сопоставлять реальность и код один к одному. Богатые объекты предметной области, value objects вместо string/int и антипаттерны DTO-оргии. const, иммутабельность, ленивая инициализация и побочные эффекты в читаемом коде. Меньше if и switch, отказ от null, быстрый провал и полиморфизм вместо флагов. Singleton, god object, shotgun surgery, feature envy и глобальное состояние — симптомы и приёмы рефакторинга. Мёртвый код, лишние абстракции, отключённые предупреждения и отложенный рефакторинг. Приватные методы, flaky-тесты, assertTrue, моки и данные — качество тестового кода и связь с разделом тестирования. Пустые catch, исключения как goto, узкие try и сообщения для пользователя. Краткие итоги раздела "Культура кода". Вопросы перед отправкой кода в репозиторий — именование, принципы, тесты, ревью и безопасность. Ответы ищите в статьях раздела.Культура написания и поддержки кода
Цикломатическая сложность и читаемость кода
MAPPER — модель кода и предметная область
Анемичные модели и примитивная одержимость
Изменяемость, побочные эффекты и неизменяемые данные
Условия, null и явные контракты
Связанность, глобалы и запахи модульности
YAGNI, быстрый провал и техдолг в коде
Тесты как часть культуры кода
Исключения и обработка ошибок в читаемом коде
Итоги
Чек-лист самопроверки