---
title: "Многословный шаблонный код"
type: "ai-smell"
slug: "verbose-boilerplate"
url: "http://localhost:3000/ru/ai-smells/verbose-boilerplate.md"
category: "Ясность"
description: "ИИ-ассистенты раздувают простую логику избыточными комментариями, защитной церемонией и скопированными почти-дубликатами вместо того, чтобы переиспользовать или извлечь существующий код, наращивая число строк без добавления ценности."
---
# Многословный шаблонный код

> ИИ-ассистенты раздувают простую логику избыточными комментариями, защитной церемонией и скопированными почти-дубликатами вместо того, чтобы переиспользовать или извлечь существующий код, наращивая число строк без добавления ценности.

## Signs and Symptoms

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

```ts
// Функция для получения полного имени пользователя
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** (переименуйте так, чтобы код документировал себя сам).

**Рефакторинг (до → после)**

```ts
// после: комментарии убраны (код самоочевиден), церемония свёрнута,
// общий 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) (https://github.com/kucherenko/jscpd)
- **SonarQube / SonarCloud** `typescript:S4144` — Functions and methods should not have identical implementations (https://rules.sonarsource.com/typescript/RSPEC-4144/)
- **SonarQube / SonarCloud** `typescript:S1192` — String literals should not be duplicated (https://rules.sonarsource.com/typescript/RSPEC-1192/)
- **SonarQube / SonarCloud** `javascript:S1871` — Two branches in a conditional structure should not have exactly the same implementation (https://rules.sonarsource.com/javascript/RSPEC-1871/)
- **ESLint (eslint-plugin-sonarjs)** `sonarjs/no-identical-functions` — Functions should not have identical implementations (https://github.com/SonarSource/eslint-plugin-sonarjs/blob/master/docs/rules/no-identical-functions.md)
- **ESLint (eslint-plugin-sonarjs)** `sonarjs/no-duplicate-string` — String literals should not be duplicated (https://github.com/SonarSource/eslint-plugin-sonarjs/blob/master/docs/rules/no-duplicate-string.md)
- **ESLint (core)** `no-useless-constructor` — Избыточный пустой конструктор / транзитный шаблонный код (https://eslint.org/docs/latest/rules/no-useless-constructor)
- **ESLint (core)** `max-lines-per-function` — Раздутая/многословная длина функции (частичный прокси) (https://eslint.org/docs/latest/rules/max-lines-per-function)
