Многословный шаблонный код.
ИИ-ассистенты раздувают простую логику избыточными комментариями, защитной церемонией и скопированными почти-дубликатами вместо того, чтобы переиспользовать или извлечь существующий код, наращивая число строк без добавления ценности.
##Signs and Symptoms
Ревьюер замечает многословный шаблонный код, когда дифф намного длиннее, чем требует задача: каждая строка озвучена комментарием, который просто повторяет её, простые выражения обёрнуты в защитные лестницы проверок null/undefined, а одна и та же форма кода появляется дважды или трижды вместо того, чтобы быть вынесенной в один хелпер. Выдаёт его высокое соотношение комментарии-к-логике и строки-к-поведению, плюс блоки, которые могли бы уместиться в три строки идиоматичного кода.
// Функция для получения полного имени пользователя
function getFullName(user: User): string {
// Проверяем, определён ли пользователь
if (user === null || user === undefined) {
// Возвращаем пустую строку, когда пользователь не передан
return "";
}
// Получаем имя, по умолчанию пустая строка
const firstName = user.firstName ? user.firstName : "";
// Получаем фамилию, по умолчанию пустая строка
const lastName = user.lastName ? user.lastName : "";
// Склеиваем имя и фамилию через пробел
const fullName = firstName + " " + lastName;
// Обрезаем пробелы и возвращаем результат
return fullName.trim();
}
Три строки поведения погребены под ~12 строками церемонии. В реальном PR вы обычно видите этот же паттерн, скопированный как getDisplayName, getLabel и getInitials, каждый из которых вручную пишет логику, уже покрытую util formatName(), — классический запах Duplicate Code. Другие приметы: пустые транзитные конструкторы/обёртки, церемония try { ... } catch (e) { throw e } и самописный валидатор email/URL, стоящий рядом с уже существующим в проекте модулем валидации.
##Reasons for the Problem
Почему модели это порождают
- Смещение к многословию при предсказании токенов. LLM обучаются на огромных объёмах учебного, взятого со Stack Overflow и написанного новичками кода, где пошаговые комментарии и явная церемония — норма. Наиболее вероятное продолжение — это самое подробно прокомментированное, а не самое лаконичное.
- RLHF вознаграждает «вид основательности». Тюнинг на полезность/многословие подталкивает модели к выводу, который выглядит полным и самообъясняющим — комментарий на каждой строке, защитные проверки повсюду, — что оценщики и пользователи вознаграждают, даже когда ценности это не добавляет.
- Нет контекста репозитория — нет переиспользования. Без окружающей кодовой базы в контексте модель не может знать, что util
formatName()илиvalidateEmail()уже существует, поэтому выводит его заново прямо по месту. Это ровно избыточная специализация по OX Security («гиперспецифичные одноразовые решения вместо обобщаемых, переиспользуемых компонентов», встречается в 80–90% ИИ-кода) и избегание рефакторингов (80–90%). - Генерация, а не консолидация. Агенты выдают свежий код на каждый промпт и никогда не возвращаются, чтобы устранить дубли. Анализ GitClear 2025 года, охвативший 211 млн изменённых строк, показал, что доля скопированного кода выросла с 8,3% (2020) до 12,3% (2024) и впервые обогнала «перемещённые» (отрефакторенные) строки, тогда как доля отрефакторенных строк упала с ~24% до 9,5%, а число дублированных блоков из 5+ строк выросло примерно в 8 раз.
- Дефолт «комментировать всё». OX Security обнаружила «комментарии повсюду» в 90–100% сгенерированного ИИ кода.
Чем это вредит
- Сопровождаемость: больше поверхности для чтения и изменения; избыточные комментарии расходятся с кодом и становятся откровенно вводящими в заблуждение (запах Comments по Фаулеру).
- Корректность и безопасность: дублированные блоки означают, что баг или уязвимость придётся чинить в N местах — «дежавю багов» по OX (70–80%). Клонированный код коррелирует с 15–50% большим числом дефектов.
- Нагрузка на ревью: большие малосодержательные диффы прячут реальные изменения и вызывают усталость ревьюера, поэтому настоящие проблемы проскальзывают.
- Нарастающий техдолг: каждый почти-дубликат усложняет следующее извлечение, укореняя проблему, которую модель не видит между файлами.
##Treatment
Тактики ревью / промптинга
- Принудите переиспользование до генерации: «Поищи в кодовой базе существующие хелперы (
formatName,validate*) и переиспользуй их; не реализуй заново». Предоставьте релевантные утилиты в контексте. - Заложите бюджет вывода: «Уложись примерно в 15 строк; никаких комментариев, повторяющих код, — только комментарии, объясняющие почему».
- Заставьте модель запускать инструменты: требуйте прогон
eslint/ruffи проверки дублированияjscpd, и пусть она отчитывается о нарушениях и исправляет их перед возвратом результата. - Просите минимальный дифф и явный шаг дедупликации: «Если какой-то блок повторяется, вынеси одну общую функцию (Extract Function) и вызывай её».
- Консолидируйте на ревью: увидев третью почти-копию, примените Extract Function, Pull Up Method или Consolidate Duplicate Conditional Fragments, а также Replace Comment with Code (переименуйте так, чтобы код документировал себя сам).
Рефакторинг (до → после)
// после: комментарии убраны (код самоочевиден), церемония свёрнута,
// общий util переиспользован вместо повторного вывода
function getFullName(user?: User): string {
return [user?.firstName, user?.lastName].filter(Boolean).join(" ");
}
И уберите дубликаты на корню — если getDisplayName/getLabel делали то же самое, удалите их и направьте вызывающий код на одну функцию (Remove Dead Code / Inline Function). Замените самописный валидатор импортом существующего модуля, а не держите параллельную копию.
Правило для каталога: любой ИИ-блок, где комментариев больше, чем строк логики, или где можно назвать существующий хелпер, который он проигнорировал, считайте кандидатом на удаление-через-переиспользование — самое быстрое лекарство от шаблонного кода обычно меньше кода, а не больше.
##Detected by
- jscpd duplication threshold (--min-tokens / --threshold) — Обнаружение копирования/вставки (порог min-tokens)
- SonarQube / SonarCloud typescript:S4144 — Functions and methods should not have identical implementations
- SonarQube / SonarCloud typescript:S1192 — String literals should not be duplicated
- SonarQube / SonarCloud javascript:S1871 — Two branches in a conditional structure should not have exactly the same implementation
- ESLint (eslint-plugin-sonarjs) sonarjs/no-identical-functions — Functions should not have identical implementations
- ESLint (eslint-plugin-sonarjs) sonarjs/no-duplicate-string — String literals should not be duplicated
- ESLint (core) no-useless-constructor — Избыточный пустой конструктор / транзитный шаблонный код
- ESLint (core) max-lines-per-function — Раздутая/многословная длина функции (частичный прокси)