Principles
August 4, 2022

KISS, DRY і YAGNI

Порядок роботи принципів на прикладі

На мою думку, ці принципи є найбільш вживаними "універсальними" принципами програмування, які є такими-собі standalone: якщо SOLID все-таки більше застосовують до механік ООП, то KISS, DRY і YAGNI можна застосувати до будь-чого, навіть не обмежуючись мовами програмування - до HTML, наприклад.

Тож давайте розглянемо тут ці принципи, щоб потім можна було флексити на співбесідах.

KISS

Принцип KISS розшифровується як Keep it Simple, Stupid, проте інколи його вживають або просто як Keep it Simple (щоб не називати людей stupid), або ж Keep it Short and Simple.

Офіційне формулювання:

Більшість систем працюють краще, якщо вони залишаються простими, а не ускладнюються. Саме тому простота має бути однією з ключових цілей в програмуванні, і варто уникати надмірної складності.

По суті, так, якщо вас спитають про цей принцип, достатньо сказати, що прості системи працюють краще і надійніше, ніж складні. Тепер давайте поговоримо про інші трактування і applications цього принципу, в тому числі у фронтенді.

1. Не треба для якоїсь маленької фічі додавати у проект цілу велику бібліотеку. Надзвичайно розповсюджено зараз, особливо на фронті - як-от з бібліотекою moment.js. Нам треба одну дату в проекті відформатувати, а ми додаємо цілу бібліотеку, тим самим додаючи цілу одну залежність, збільшуючи розмір бандлу, збільшуючи кількість потенційних вразливостей у проекті і так далі.

2. (перетинається з YAGNI). Не варто перевантажувати інтерфейс - надмірно розбивати один метод на купу інших, виносити певний хардкод в конфігураційні файли, додавати можливість налаштування чомусь простому...
Приклад. Є у нас бекенд, який запускається на строго визначеному порті (ну максимум додали можливість задавати порт у конфіг-файлі). І ми такі "ну блін, це ж не зовсім зручно, давайте напишемо логіку для автоматичного знаходження першого вільного порта і будемо біндитись туди, це ж краще". Ну зробили так. А потім адміни голову ламають, як наш бекенд порт вибирає собі, мають налаштовувати фаєрвол для всіх портів одразу і т.д. По-перше, не варто було так робити - треба було по-простому - конфіг файл і все. По-друге, якби клієнту знадобився такий автовибір порту, він би сам цю логіку на своїй стороні написав.

3. Не потрібно оптимізовувати проект, економлячи кілька наносекунд. Швидш за все, ця оптимізація зробить з коду кашу, і він точно не буде short and simple. До цього ж відноситься і реалізація астрономічної точності математичних розрахунків на неастрономічних проектах.

4. Що більше стрічок коду у проекті, то більше в ньому потенційних багів, тому що кількість багів у проекті прямо пропорційна кількості стрічок коду.

5. Насправді, кожен початківець стикається з результатом "невиконання" цього принципу тоді, коли начитавшись розумної літератури, починає новий проект. Стільки всяких ідей крутиться в голові, треба ж зробити так, щоб у майбутньому було простіше розширяти проект, треба продумати всі моменти... Так, якщо цього не зробити, то у вас вийде Фейсбук, технічний борг у якому хрін випилиш. Але якщо упоротись - ви проект так і не закінчите. Тому варто шукати золоту середину.

До речі, цікавий факт: принцип зʼявився у 1960 році в колах ВМС США.

YAGNI

Розшифровується як You Aren't Gonna Need It.

Офіційне формулювання:

Можливості, які не описані у вимогах системи, не повинні бути реалізовані.

Тобто коли пишемо код, треба бути впевненими, що цей код обовʼязково нам знадобиться. Якщо думаємо - що код знадобиться пізніше - негайно припиняємо. Якщо ж займаємось рефакторингом - не варто боятись видаляти сутності, що не використовуються - раніше були часи, коли ці сутності були потрібні, тепер ці часи минули.

Ну і знову ж, поговоримо про реальні приклади і застосування цього принципу.

1. Замовник не має оплачувати те, чого він не замовляв. Навіть якщо ви думаєте, що якась штука буде йому корисною, він має про це знати ще до того, як ви почнете її писати, бо, повірте, у майбутньому це гарантовано вилізе боком.

2. Поговоримо про "вилізе боком" з попереднього пункту. Ну от ви написали якусь додаткову логіку "на майбутнє". Потім замовник вам дає завдання написати нову фічу, і ви такі: "блін, вона ж повністю протиречить тій додатковій логіці, що я написав раніше...". І це ще ок, ви просто видалите її. Але ж може бути, що ви написали новий модуль типу "на майбутнє", і вже використали його десь. Так тепер вам ще і попереднє все переписувати, перетестовувати і так далі.

3. Написавши нову штуку, на вас лягає зобовʼязання її підтримувати: писати тести, документацію, підганяти конфігурацію і слідкувати за помилками, пояснювати юзерам, як вашою штукою користуватись, і так далі. Воно вам треба?

4. Якщо проект fixed-price, а ви додали якусь нову штуку, якої не було в ТЗ, замовник з легкістю може почати вами маніпулювати: "ой, а ви ось це зробили, але воно взагалі не так повинно працювати, допишіть будь ласка". Таким чином, ви додали роботи, тобто витратили гроші вашої компанії, і цілком можливо навіть вивели її в мінус, тому варто чекати небажаних гостей.

DRY

Ну тут все просто: Don't Repeat Yourself. Принцип був сформований у відомій книзі "Програміст-прагматик" Ендрю Ханта і Дейва Томаса. Насправді, цей принцип не стільки про дублювання коду (бо про це і так каже майже будь-який інший принцип), скільки про єдине джерело даних (Single Source of Truth).

Офіційне формулювання:

Будь-яка інформація повинна мати єдине, однозначне і авторитетне представлення в системі.

Безліч разів бачив, що дані в додатку дублюються просто для того, щоб було "простіше працювати", типу дані однакові, але зберігаються в різних форматах. Я теж коли писав свої проекти, думав: "блін, нашо мені кожен раз конвертувати цю стрічку в мій формат, якщо можна просто в таблиці поруч нове поле зробити і туди це все класти". Насправді, нове поле поруч з оригінальним - це ще не страшно, бо його видно. А ось коли у нас одні і ті самі дані в різних таблицях - це вже страшно. Та, насправді, навіть коли вони поруч, дуже просто забути продублювати операції для роботи з ними. У будь-якому разі, рано чи пізно ці дані розсинхронізуються, і стане погано.

Так, принцип також застосовують до коду, але я хотів би тут перерахувати ті виключення, при яких цей принцип можна і треба не виконувати.

Ну ось наприклад ви пишете фронт і бек - валідація. Її просто обовʼязково дублювати, це загальновідоме правило і без цього ніяк не можна, бо інакше ви або змушуєте клієнт і сервер страждати від величезної кількості запитів (якщо валідувати тільки на бекенді), або ж порушуєте найпростіші принципи компʼютерної безпеки (валідуючи лише на фронті).

Також як чудовий приклад виключення з принципу DRY можна назвати компʼютерні ігри. Зазвичай у онлайн-іграх деякий код дублюють і на клієнті, і на сервері, заради оптимізації. Адже без такої оптимізації навіть гравці з відносно непоганим пінгом бачили б слайдшоу замість плавного руху персонажів, до прикладу (ну типу клієнт сам обраховує приблизні вектори руху персонажів і рухає їх, поки достовірна інформація йде з сервера, і виходить імітація відсутності затримок).

Висновки

KISS (Keep It Simple, Stupid): більшість систем працюють краще, якщо вони залишаються простими, а не ускладнюються. Суть: треба писати по-дубовому, не створювати мільйон абстракцій і конфігурацій. Також не варто лише задля маленької фічі запихати в проект величезну бібліотеку.
YAGNI (You Aren't Gonna Need It): можливості, які не описані у вимогах системи, не повинні бути реалізовані. Суть: не варто реалізовувати те, що ви думаєте, полегшить роботу або знадобиться у майбутньому.
DRY (Don't Repeat Yourself): будь-яка інформація повинна мати єдине, однозначне і авторитетне представлення в системі. Суть: не допускайте ситуацій, коли ви зберігаєте одні і ті ж дані (просто в різних формах) у різних місцях в системі.

Джерела

Коротко про KISS, YAGNI і DRY на Хабрі: https://habr.com/ru/company/itelma/blog/546372/

KISS by Sergey Nemchinskiy: https://www.youtube.com/watch?v=rix-fkrloq4

YAGNI by Sergey Nemchinskiy: https://www.youtube.com/watch?v=Ot2eB07rjcI

DRY by Sergey Nemchinskiy: https://www.youtube.com/watch?v=NWemqNMCesQ