Чиста функція:
- для тих самих аргументів завжди повертає той самий результат;
- не має побічних ефектів - не змінює нічого за своїми межами й не залежить від прихованого стану.
Побічні ефекти: запис у базу чи файл, HTTP-запит, надсилання листа, зміна глобальних чи статичних змінних, зміна переданих за посиланням аргументів, читання поточного часу, випадкових чисел, $_GET, конфігурації.
// Нечиста: залежить від часу й бази
function isExpired(int $subscriptionId): bool
{
$sub = Subscription::find($subscriptionId);
return $sub->ends_at < now();
}
// Чиста: усе потрібне - в аргументах
function isExpired(DateTimeImmutable $endsAt, DateTimeImmutable $now): bool
{
return $endsAt < $now;
}
Чому це важливо для тестів:
- Чисту функцію тестують одним рядком: дав вхід - перевірив вихід. Без бази, моків, фейкового часу.
- Тести швидкі й стабільні: немає залежності від порядку, часу запуску, стану бази.
- Легко перевірити граничні випадки - просто передати інші аргументи.
Як застосувати на практиці - «функціональне ядро, імперативна оболонка»:
- Ядро - чисті обчислення бізнес-правил: розрахунок ціни зі знижками, перевірка правил, переходи станів, форматування.
- Оболонка - тонкий шар, що збирає дані (база, запит, час), викликає ядро й виконує побічні ефекти з результатом (зберегти, надіслати).
Більшість логіки опиняється в легко тестованому ядрі, а оболонку покривають кількома інтеграційними тестами.
Не все має бути чистим: застосунок існує заради побічних ефектів. Мета - не прибрати їх, а відокремити й зібрати на краях, щоб логіка між ними лишалася простою для перевірки.