Контекстно-слепое именование.
ИИ-ассистент даёт новому коду обобщённые заглушки-имена или новое соглашение, игнорируя существующие идентификаторы и доменный словарь репозитория, что подрывает читаемость и плодит дублирующиеся, неверно описанные понятия.
##Признаки и симптомы
Ревьюер замечает контекстно-слепое именование, когда имена в созданном ИИ диффе локально правдоподобны, но оторваны от окружающего репозитория. Характерные признаки:
- Обобщённые заглушки в доменном коде:
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 — Обнаруживает дублированные блоки, возникающие, когда переименованное понятие дублирует существующее