DRY в разработке: когда дублирование лучше абстракции
Два одинаковых фрагмента кода ещё не означают, что их нужно объединить. Разбираем, как отличить копию одного бизнес-правила от похожей логики, которая будет меняться по-разному, и когда рефакторинг окупится.

Два фрагмента похожи — и рука тянется вынести их в общую функцию. Но если похожие на вид правила меняются по разным причинам, абстракция свяжет их сильнее, чем нужно: поправка для одного сценария сломает другой. Разберём, как на практике отличать дублирование знания от совпадения кода, когда объединять поведение и чем рискует команда, если сделать это слишком рано.
DRY — про знания, а не про одинаковые строки
DRY расшифровывают как Don’t Repeat Yourself — «не повторяйте себя». В инженерной практике это не запрет на похожие участки кода. Важнее понять, повторяет ли система одно и то же знание: правило, решение или ограничение, которое должно меняться согласованно.
Представьте, что сайт и административная панель показывают одно и то же состояние заказа. Если оба места самостоятельно определяют, когда заказ считается оплаченным, это потенциальное дублирование правила. Когда правило изменится, его придётся обновить в двух местах; одно из них легко забыть.
А теперь — два похожих поля ввода в разных формах. Одно ограничивает длину названия товара, другое — внутреннего комментария. Пока у них одинаковый лимит, это ещё не значит, что у них одинаковое требование. Если бизнес-правила для этих полей независимы, общий валидатор может создать ненужную связь.
Коротко о термине. Источник истины — место, которому система или команда доверяет как единственному авторитетному описанию конкретного правила. Если одинаковое правило приходится менять в нескольких независимых местах, единого источника истины, скорее всего, нет.
Устраняйте повторение правила, а не сходство, которое пока видно только в строках кода.
Три подхода — и цена каждого
У разработчика обычно есть три варианта: оставить отдельные реализации, сразу объединить их или дождаться дополнительного подтверждения, а затем вынести общее поведение. Ни один не подходит для всех случаев. Выбор зависит от того, насколько точно вы знаете причины будущих изменений.
| Подход | Когда уместен | Цена |
|---|---|---|
| Оставить отдельно | Сценарии пока похожи только внешне или правила меняются независимо | Повторённое знание придётся обновлять в нескольких местах, если оно всё-таки общее |
| Объединить сразу | Есть одно правило и подтверждённые причины менять его целиком | Меньше мест для правок, но общая функция может получить ветвления и лишние параметры |
| Подождать и объединить позже | Похожий код появился недавно, а требования ещё не проверены практикой | Временно поддерживаются копии; зато решение об абстракции опирается на реальные изменения |
Полезная эвристика — «правило трёх»: при первом появлении решения реализуйте сценарий, при втором присмотритесь, при третьем ищите устойчивое общее правило. Это не стандарт и не математический порог. Если две реализации заведомо обслуживают одно требование, ждать третьей копии незачем; если причины изменений разные, наличие трёх похожих фрагментов само по себе не делает их хорошей абстракцией.
Как принимать решение до рефакторинга
Сравните не внешний вид кода, а ответственность и ожидаемые изменения. Перед тем как создавать общий модуль, пройдите короткую проверку:
- Назовите правило. Сформулируйте одним предложением, что именно делает повторённая логика. «Обе функции проверяют данные» — слишком широко; «обе определяют, можно ли перевести заказ в состояние “оплачен”» — уже проверяемое утверждение.
- Найдите владельца требования. Кто решает, как правило должно работать: одна команда, продуктовая область или разные владельцы? Если изменения приходят из независимых источников, общая реализация может стать точкой согласования и задержки.
- Посмотрите историю правок. Если похожие участки менялись вместе по одному требованию, это аргумент за общую реализацию. Если один меняли, а второй оставляли без изменений намеренно, сходство может быть случайным.
- Сравните будущие варианты поведения. Выпишите, что произойдёт, если одному сценарию понадобится исключение. Если уже сейчас вы ожидаете отдельные условия, параметры и флаги, абстракция может лишь спрятать две независимые логики в одном месте.
- Оцените цену ошибки. Для правила, влияющего на деньги, доступ или состояние записи, пропущенное обновление может быть дороже дублирования. Но и общий компонент с неверным поведением способен распространить ошибку сразу на несколько сценариев.
Проверка пройдена, если вы можете назвать общее правило, его владельца и ожидаемое согласованное изменение. Тогда объединение сокращает число точек, которые нужно держать в синхронном состоянии. Если ответы расходятся, оставьте реализации независимыми и поясните в коде или ревью, почему это сознательное решение.
Когда общая абстракция начинает мешать
Самый заметный сигнал — общая функция обрастает флагами вроде «для админки», «для экспорта» или «разрешить исключение». Каждый флаг может быть оправдан, но их скопление часто показывает, что несколько сценариев с разными правилами слили ради похожих строк.
Другой сигнал — изменение одного потребителя требует проверять все остальные. Общий модуль снижает стоимость согласованной правки, но повышает цену локальной: автор изменения может не знать, что его решение затронет ещё несколько частей системы.
Опасна и абстракция по форме данных. Два объекта могут иметь одинаковые поля сегодня, но разный смысл и разные циклы жизни. Объединение только потому, что у них совпадает структура, превращает изменение модели в задачу для всех её потребителей.
Проверяйте границу на тестах, которые фиксируют поведение каждого сценария отдельно. Если после объединения тесты требуют множества комбинаций параметров, проверьте, не объединены ли под одним именем независимые правила. Тесты не докажут, что абстракция выбрана правильно, но покажут, какие контракты она связывает.
Что написать в ревью
Вместо «нарушен DRY» укажите конкретную причину: «Оба места задают один порог, и в прошлом он менялся одновременно; предлагаю хранить правило в одном месте». Если считаете, что объединять рано, сформулируйте обратное: «Проверки выглядят похожими, но у полей разные требования; общий валидатор заставит менять независимые сценарии вместе».
Такие замечания обсуждают не количество повторённых строк, а цену двух вариантов. Это помогает автору и рецензенту спорить о требованиях, а не о личных предпочтениях.
Как понять, что выбрали неудачно
Решение не обязано быть идеальным с первого коммита. Важно заметить стоимость ошибки до того, как она расползлась по проекту.
- После выноса функции новые изменения требуют всё больше специальных параметров.
- Команды регулярно правят общий код ради одного потребителя и боятся затронуть остальные.
- Копии остались, хотя их обновляют по одному и тому же требованию, и исправления периодически расходятся.
- Ревью обсуждает, какая часть системы «главнее», вместо того чтобы проверять конкретный контракт.
Если проблема в общей абстракции, не обязательно возвращаться к копированию один к одному. Иногда правильнее разделить компонент по независимым правилам и оставить общими только действительно общие части. Если проблема в копиях, сначала зафиксируйте одинаковое поведение тестами, затем объединяйте. Так проще отличить безопасный рефакторинг от изменения требований.
У этого материала нет версии языка или библиотеки: DRY — принцип организации кода, а не API. Конкретные способы выделения общего поведения зависят от языка, архитектуры проекта и соглашений команды.
Что сделать дальше
Возьмите один повторяющийся фрагмент из ближайшего изменения и выпишите: какое правило он выражает, кто владеет требованием и должны ли сценарии меняться вместе. Если на все три вопроса есть ясный ответ, выберите общую реализацию и добавьте тесты на каждый потребитель. Если нет — пока оставьте код отдельно, зафиксируйте причину в ревью и вернитесь к решению, когда появится следующий реальный случай.
Логотип и фирменный стиль: Три концепции разного характера, палитра с обоснованием, проверка на читаемость в малом размере и в один цвет.
Спросить за 60 ₽Источники: Martin Fowler — The Rule of Three
Частые вопросы
Если я вижу одинаковые строки кода, разве не нужно сразу вынести их в функцию?
Когда уже достаточно повторений, чтобы применить правило трёх?
Что делать, если я уже вынес похожий код и функция обросла флагами?
Не опаснее ли оставить дублирование и забыть обновить одну копию?
Нужно ли применять DRY к тестам и конфигурации?
Можно ли заранее понять, что абстракция будет удачной?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.