KISS и YAGNI на практике
Допустим, разработчик заранее предполагает, что скидки когда-нибудь будут загружаться из базы, файла, внешнего API и панели администратора. Он создаёт общий движок правил, хотя сейчас существует только одно условие.
interface DiscountProvider {
getRules(context): Rule[];
}
class DiscountProviderFactory {
create(source) {
if (source === "database") { /* ... */ }
if (source === "remote") { /* ... */ }
if (source === "config") { /* ... */ }
}
}
// All three variants are still imaginaryФормально архитектура выглядит гибкой. Фактически в проекте появились интерфейс, реестр, фабрика и формат конфигурации. Каждый новый участник команды сначала изучает механизм, а уже потом - простое правило скидки. Что предлагает KISS: оставить правило явной функцией с понятным именем, входом и результатом. Чтобы проверить скидку, достаточно открыть один файл.
function calculateDiscount(subtotal) {
if (subtotal <= 10000) {
return 0;
}
return subtotal * 0.1;
}Что добавляет YAGNI: не создавать несколько источников правил до появления хотя бы второго реального источника. При этом решение не должно мешать будущему изменению - и простая функция подходит под оба условия сразу.
Разницу удобно держать как таблицу сигналов: одно и то же будущее изменение можно готовить нормально или преждевременно.
Сигнал · Нормальная подготовка · Преждевременная архитектура
- Будущее изменение - Изолировать формулу в именованной функции; Создать платформу для неизвестных вариантов
- Тестирование - Проверить функцию несколькими значениями; Подменять четыре слоя ради одной формулы
- Расширение - Менять структуру после второго реального сценария; Проектировать все возможные сценарии заранее
KISS и YAGNI считают решения, а не строки. Простая функция и гибкий движок могут быть одинаковой длины. Разница в том, сколько решений и слоёв придётся удерживать в голове, чтобы изменить одну формулу.
Пока правил мало, дублирование не проблема. Но как только похожий код появляется во втором месте, вступает DRY - и тут важно понять его правильно.