Разговор о чистых функциях часто уходит в теорию, хотя практический вопрос намного проще: можно ли безопасно понимать, тестировать и повторно использовать этот кусок кода, не вспоминая про скрытое состояние вокруг него.
Чистые функции полезны потому, что они предсказуемы. Они не зависят от внешнего мира, не меняют его и возвращают один и тот же результат для одинаковых аргументов. Как только код начинает читать глобальные переменные, генерировать случайные значения или менять внешнее состояние, предсказуемость падает.
Ниже разберем, как быстро различать чистые и нечистые функции на практике и где побочным эффектам вообще место в приложении.
Обновлено 17 марта 2026: материал переписан вокруг инженерных критериев чистоты, а не только вокруг формального определения.
Что делает функцию чистой
У чистой функции есть два признака:
- одинаковые аргументы всегда дают одинаковый результат;
- при вызове не происходит побочных эффектов вне самой функции.
Простейший пример:
const increment = (value) => value + 1;
increment(2); // 3
increment(2); // 3Такая функция удобна для тестирования, кеширования и композиции, потому что она полностью определяется своими входными данными.
Где код становится нечистым
Нечистота бывает не в одной форме. Чаще всего проблема проявляется в одном из четырех сценариев.
1. Зависимость от внешнего состояния
const COST_OF_ITEM = 250;
const totalPrice = (quantity) => COST_OF_ITEM * quantity;Функция выглядит безобидно, но ее результат зависит не только от quantity. Если глобальная константа или внешняя конфигурация изменится, поведение тоже изменится.
2. Нестабильный результат при одинаковом вводе
const generateId = () => Math.floor(Math.random() * 10000);Такую функцию нельзя назвать чистой, потому что один и тот же вызов возвращает разные значения. То же самое касается Date.now() и большинства обращений к случайности, времени или сети.
3. Мутация внешних данных
const removeLastItem = (list) => {
list.pop();
return list;
};Проблема здесь не в самом pop, а в том, что функция меняет переданный массив. Это создает скрытый эффект в другом месте программы.
4. Побочные эффекты во внешний мир
const logger = (message) => {
console.log(message);
};console.log, запись в localStorage, DOM-обновления, сетевые вызовы и работа с файлами меняют состояние вне функции. Это нормальные операции, но они делают функцию нечистой.
Как сделать код чище на практике
Главная идея не в том, чтобы убрать все побочные эффекты из приложения. Это невозможно. Идея в том, чтобы изолировать их.
Например, генерацию id лучше вынести наружу и передавать результат как аргумент.
const createUser = (id, name, age) => ({
id,
name,
age,
});
const id = Math.floor(Math.random() * 10000);
const user = createUser(id, "Alex", 28);Теперь сама функция createUser чистая. Нечистая часть осталась на границе, где мы действительно ожидаем side effect.
То же самое работает с конфигами и временем:
- лучше передать
price, чем читать глобальную переменную; - лучше передать
now, чем вызыватьDate.now()глубоко внутри логики; - лучше вернуть новый массив, чем мутировать старый.
Почему это важно не только для функционального программирования
Чистые функции полезны даже если ты не пишешь “функциональный” код.
Они:
- проще тестируются;
- легче компонуются в pipeline;
- снижают количество случайных регрессий;
- лучше подходят для кэширования и memoization;
- понятнее при ревью, потому что меньше скрытых зависимостей.
Именно поэтому тема чистоты важна не как часть философии, а как инструмент для читаемости и надежности.
Где нечистым функциям нормально жить
Побочные эффекты нужны. Вопрос не в их существовании, а в том, где они расположены.
Лучше всего держать их ближе к границам системы:
- в обработчиках событий;
- в сетевом слое;
- в адаптерах к хранилищу;
- в точках интеграции с браузером или Node.js.
А внутри доменной логики, вычислений и преобразований данных лучше сохранять функции максимально чистыми. Тогда поведение системы проще проверять и собирать из маленьких частей.
Короткий чеклист
Если сомневаешься, задай функции четыре вопроса:
- читает ли она что-то, чего нет в ее аргументах;
- меняет ли она данные, переданные снаружи;
- пишет ли она что-то во внешний мир;
- вернет ли она тот же результат завтра при тех же аргументах.
Если хотя бы на один вопрос ответ “да”, функция нечистая или, как минимум, не полностью чистая.
Итог
Чистая функция — это не статус “идеального” кода, а удобный инженерный контракт: все нужное приходит через аргументы, а результат предсказуем. Чем больше таких функций в логике приложения, тем легче код читать, тестировать и безопасно менять.
Самый практичный следующий шаг — не пытаться сделать чистым все подряд, а начать выносить побочные эффекты к границам системы. Даже одно такое разделение быстро делает кодовую базу понятнее.