Логотип ConstructiCat
CodeBust.
Навигация по разделу ▾

Контекстно-слепое именование.

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

##Признаки и симптомы

Ревьюер замечает контекстно-слепое именование, когда имена в созданном ИИ диффе локально правдоподобны, но оторваны от окружающего репозитория. Характерные признаки:

  • Обобщённые заглушки в доменном коде: data, result, temp, item, obj, payload, response, value, handleStuff, processData там, где модуль уже говорит на конкретном языке (grossPremium, Customer, getCustomerById).
  • Дрейф соглашений: символ в snake_case, помещённый в файл на camelCase, отсутствующий булев префикс is/has или новый CRUD-глагол (fetch*) в кодовой базе, стандартизированной на get*. Каждый дифф следует тому стилю, который встретил последним, вводя четвёртое соглашение. Затем пятое.
  • Разрастание синонимов / дублирующиеся понятия: ИИ выдумывает fetchUser, когда getCustomerById уже существует, или смешивает customer/client/user для одной сущности — реализуя заново вместо переиспользования.
  • Имена, описывающие механизм, а не намерение — или лгущие о поведении: processData(), который на деле вычисляет налог с продаж. Агенты (и следующий агент) читают processData() и действуют так, будто это имя рассказывает всю историю, поэтому неверный смысл распространяется на каждое место вызова.
// Репозиторий уже экспортирует getCustomerById(id: CustomerId): Promise<Customer>
// ИИ добавляет почти-дубликат с контекстно-слепыми именами:
async function fetchData(id: string) {          // обобщённый глагол, размытый тип
  const result = await db.query("select * from customers where id = $1", [id]);
  const temp = result.rows[0];                  // 'temp' скрывает, что это Customer
  return temp;                                  // ничто здесь не говорит "Customer"
}

##Причины проблемы

Почему модели это порождают

  • Смещение к частотности следующего токена. По всему обучающему корпусу data/result/temp/foo — одни из самых частых идентификаторов, особенно в учебном и шаблонном коде, который LLM усваивают в больших объёмах. Генерация статистически среднего имени — это ровно то, на что оптимизирован предсказатель следующего токена, и это сглаживает доменный смысл.
  • Отсутствие (или усечённый) контекст репозитория. Модель редко видит глоссарий соседнего модуля или существующий getCustomerById. В обзоре отчёта GitClear 2025 года DevClass отмечает, что ассистенты реже предлагают переиспользовать похожую функцию из другого места — отчасти из-за ограниченного размера контекста (DevClass о GitClear 2025).
  • Локальная оптимизация / слабый инстинкт рефакторинга. Каждый ход оптимизирует непосредственный промпт, а не кодовую базу в целом. OX Security обнаружила избегание рефакторинга — ИИ «отлично генерирует новый код, но никогда не возвращается, чтобы улучшить архитектуру» — в 80–90% изученного ИИ-кода (отчёт OX).
  • Устаревание из-за порога обучения. Соглашения и имена API из более старых корпусов всплывают вновь даже после того, как проект ушёл вперёд.

Почему это вредит

  • Читаемость/сопровождаемость: имена — это первичная документация кодовой базы; обобщённые имена вынуждают каждого читателя заново выводить намерение из тела.
  • Дублирование и дефекты: переименование понятия порождает параллельную реализацию. Анализ GitClear 2025 года (211 млн изменённых строк, 2020–2024) показал, что доля строк, связанных с рефакторингом («перемещённых»), упала с 25% изменённых строк в 2021 году до менее чем 10% в 2024-м, а доля скопированных/вставленных строк за тот же период выросла с 8,3% до 12,3%; 2024 год стал первым, когда скопированных строк оказалось больше, чем перемещённых, а число блоков из пяти и более дублирующихся строк за 2024 год выросло примерно в восемь раз (GitClear 2025).
  • Петля обратной связи агента (специфичный для ИИ вред): имена — это интерфейс, который следующий агент воспринимает буквально. Как сказано в Encyclopedia of Agentic Coding Patterns, ИИ-агент считает имена, найденные в кодовой базе, своим основным сигналом о том, что делает тот или иной элемент, а «непоследовательные имена в существующем коде порождают непоследовательные имена в сгенерированном» (AI Pattern Book).
  • Нагрузка на ревью и корректность: ревьюерам приходится мысленно отображать temp/data обратно на доменные понятия, что прячет ошибки; вводящие в заблуждение имена приводят к неправильному использованию в местах вызова.
  • Слепые зоны безопасности/аудита: секрет или токен, припаркованный в переменной с именем data/tmp, проскальзывает мимо grep-ов по именам и внимания ревью.

Примечание: статические инструменты хорошо ловят локальные, явные нарушения именования (регистр, длина, запрещённые слова), но соответствие имени доменному смыслу зависит от намерения и в значительной мере ускользает от автоматического обнаружения.

##Лечение

Тактики ревью и промптинга

  • Подайте соглашения и глоссарий в контекст. Держите краткое руководство по именованию (регистр, булевы префиксы, CRUD-глаголы, доменные термины) в CLAUDE.md/документах по стилю и требуйте, чтобы модель ему следовала. Применяйте термины доменного глоссария последовательно, чтобы синонимы схлопнулись к одному слову.
  • Принуждайте к переиспользованию до создания. Промпт: "Найди в репозитории существующую функцию/тип для этого, прежде чем добавлять новую; переиспользуй её". Это прямо противодействует ошибке дублирования понятий, на которую указывают GitClear и OX.
  • Называйте вещи в промпте. "Назови обработчик createRefund" лучше, чем "добавь обработку возвратов". Указывайте доменные имена (monthlyRevenue, а не float1).
  • Требуйте запуска линтера + сканирования дублирования на диффе (naming-convention + id-denylist + jscpd) и пусть модель исправляет нарушения, а не вы вручную.

Рефакторинг — примените «Переименование метода» (и то же самое для переменных), чтобы исправить вводящие в заблуждение имена, и устраните «Дублирование кода», вызывая существующий символ вместо нового.

До:

async function fetchData(id: string) {
  const result = await db.query("select * from customers where id = $1", [id]);
  const temp = result.rows[0];
  return temp;
}

После (удалите почти-дубликат и вызовите существующую функцию репозитория с доменным типом):

// Не делай повторный запрос — переиспользуй getCustomerById и сохрани доменный словарь.
const customer = await getCustomerById(id as CustomerId);

Если вводящее в заблуждение имя уже попало в продакшен, переименуйте его в соответствие с поведением (processData → calculateSalesTax) до того, как строить на нём, чтобы последующие агенты и люди унаследовали верный сигнал.

##Обнаружение анализаторами

  • ESLint (core) id-denylist — Запрет указанных идентификаторов
  • ESLint (core) id-length — Обеспечение минимальной/максимальной длины идентификатора
  • typescript-eslint @typescript-eslint/naming-convention — Обеспечение соглашений об именовании (регистр/префиксы)
  • eslint-plugin-unicorn unicorn/prevent-abbreviations — Предотвращение сокращений / чрезмерно обобщённых имён
  • SonarQube / SonarSource typescript:S117 — Имена локальных переменных и параметров должны соответствовать соглашению об именовании
  • jscpd --min-lines / --min-tokens — Обнаруживает дублированные блоки, возникающие, когда переименованное понятие дублирует существующее