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

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