Оптимизм не оправдался, или «Никогда такого не было — и вот опять»

Ничего не предвещало, но вот я вновь сажусь за клавиатуру. После цикла апокалиптических прогнозов я неосторожно дал волю природному оптимизму и написал статью «Другая сторона медали». Там я нашёл посреди общей катастрофы локальный позитив: нейросети проверяют архитектуру лучше многих «квази-сеньоров», значит, некомпетентность руководителей станет видна, и рано или поздно начнётся очищение.
Но, как оказалось, история В и Л не закончилась. И вот у меня для вас новая байка.
Жил-был монолит
Жил-был монолит. Классический GOD-класс пешки на карте: движение, получение урона, атака, бустеры, визуал и ещё десяток обязанностей в одном файле. Такой код хорошо знаком всем, кто работал в геймдеве: чтобы поменять скорость анимации, приходится читать метод расчёта урона, потому что кто-то когда-то решил "асинхронная задержка регистрации урона до правильного момента в анимации - это просто и наглядно".
Мидлу В поставили задачу облагородить черты этого монолита: расколоть на пакеты и модули, документировать, но не злоупотреблять private и internal, ведь "скрытие - признак плохого кода!".
Что, не ожидали? Я тоже... Но да, "в каждой избушке - свои погремушки". Хотя бы монолит дробить начали - уже праздник.
Работа шла понемногу, как и положено при рефакторинге живого проекта. Из общего класса один за другим выносились движение, получение урона, атака. Чтобы на примере было: получение урона уехало в отдельный типизированный интерфейс, способность «могу получать урон». Смысл простой: урон касается только тех, кто умеет его получать, не заставляя все типы пешек прописывать параметры получения урона, даже если они его не получают, и входит в систему ровно в одной точке. Кто работал с крупными проектами, понимает цену такого решения: одна точка входа — это одно место для отладки, одно место для правки, одно место, где можно поставить брейкпоинт и увидеть всё.
Месяц такой работы — это не месяц написания кода. Это месяц аккуратного переноса без поломок, согласования зависимостей, проверки, что после каждого шага всё ещё работает. Впереди забрезжило светлое будущее...
Но тут тимлид Л открыл для себя вайбкодинг с Claude Code.
Задача не для всех
Само по себе это ещё не беда. Инструмент хороший, я сам им пользуюсь. Беда в том, что одновременно на отдел легла задача собрать механику проверки условий срабатывания для самых разных ситуаций. «Сработать, когда дистанция до цели меньше N». «Сработать после третьего попадания». «Сработать, когда в радиусе больше двух противников». И так далее, с перспективой бесконечного расширения, потому что геймдизайнеры придумывают условия быстрее, чем программисты успевают их реализовывать.
Это задача из тех, где архитектура решает всё. Условия — классический пример открытого множества: сегодня их пять, через полгода пятьдесят. Если в ядре системы нет абстракции, которая позволяет добавлять новый тип условия, не трогая уже написанные, через полгода получится гарантированный монолит. Чтобы спроектировать такую систему, нужны большой опыт, серьёзные навыки и глубокое понимание проблем масштабирования.
Поэтому Л мужественно взял эту задачу на себя.
Позже В спросит, как работает его система, и получит ответ:
«Да без понятия, я её Клоду целиком отдал. Клод хорошо пишет, так что там точно всё рабочее».

И вот итог
Claude действительно написал полностью рабочий код. Здесь к нему претензий нет: задача сформулирована — задача выполнена.
Попутно, всё разделённое собрали обратно в GOD-монолит. Причём вынесенные ранее пакеты и модули никто не удалил, они продолжают существовать параллельно. Получение урона теперь входит в систему двумя путями с одинаковой сигнатурой, и какой из них основной, из кода не понять. Почему так вышло - история умалчивает. Но факт остается фактом - с новой механикой в проект пришла шизофрения.
Дальше - больше. Условие проверяется методом с перегрузкой под каждый конкретный случай. Условно это выглядит так:
bool Check(DistanceCondition condition, ...);
bool Check(HitCountCondition condition, ...);
bool Check(TargetsInRadiusCondition condition, ...);
// следующая — когда геймдизайнер придумает следующее условие
Каждая новая перегрузка, разумеется, дописывается в ядро пакета. Добавить условие, не трогая ядро, нельзя. Контекст проверки — закрытый набор параметров из домена атаки / движения / повреждения и т.п., поэтому условие, которому нужны другие данные, потребует менять сигнатуру общего интерфейса юнита, а следом все его реализации и все вызовы.
В проекте теперь заложено несколько мин. Одни и те же данные текут двумя путями, и рано или поздно кто-то поправит один путь, не зная о втором. Месяц работы по приведению кода во вменяемый вид обнулён. Зато задача закрыта в рекордные сроки, и по всем метрикам скорости Л выглядит героем.
Дуалистичный молоток
Молоток в руках краснодеревщика и молоток в руках алкаша Василия — это разные молотки, даже если это один и тот же молоток.
Самое смешное в этой истории: тот же Claude Code в руках мидла В сам подсвечивает ошибки реализации и предлагает адекватные точки ввода абстракций. Когда В прогнал через модель вопрос о дизайне системы условий, он получил ровно то, что получил бы от толкового архитектора: обобщённый интерфейс условия в независимом модуле и доменное замыкание на стороне юнитов.
// в Conditions (лист, зависимостей нет)
public interface ICondition<TContext> { bool Check(TContext context); }
// в Units
public interface IUnitCondition : ICondition<UnitConditionContext> { ... }
Модель та же, проект тот же. Разница только в том, кто сидит за клавиатурой и в типе запроса. В спрашивает: «Правильно ли это устроено?» Л поручает: «Сделай, чтобы работало». На первый запрос модель отвечает принципами, на второй — минимальными изменениями в указанном месте. А кодовая база для неё — фактическая спецификация: если в общем интерфейсе уже лежит десяток разнородных методов, одиннадцатый выглядит последовательным продолжением, а не нарушением. И хотя, вроде бы, ничто не мешает Л задать такой же вопрос и получить такой же ответ, на практике этого не происходит. Потому что чтобы понять, что нужно вообще что-то спросить, нужны компетенции, которых Л, очевидно, не хватает.
Нейросеть — инструмент, а не действующее лицо. Она не приносит в задачу вкус к правильной архитектуре. Она усиливает то, что уже есть у оператора. И если все, что есть у оператора - это руки, произрастающие не из плеч, то и результат... будет пахнуть.
Другой стороны у этой медали нет
В прошлой статье я предположил, что рядом с моделью, которая отвечает правильно, некомпетентность становится видна. Мой оптимизм, традиционно, оказался необоснован: квази-сеньор модель ни о чём не спрашивает. Он ей поручает.
В кривых руках нейросеть не порождает «усреднённо годный продукт». Она масштабирует некомпетентность владельца и прячет её глубже, чем спрятал бы он сам. Раньше плохой код было видно с первого взгляда: кривые имена, копипаста, закомментированные куски, // TODO: разобраться. Теперь у него аккуратные имена, опрятная структура и подробные комментарии. Чтобы разглядеть проблему, нужно ревью архитектуры. А проводить его должен тот, кто и поставил задачу... Круг некомпетентности замкнулся.
Чайка-менеджмент на стероидах
Все, кто поработал в индустрии, знают чайка-менеджмент: прилетел, наорал, насрал, улетел. У такого стиля всегда был естественный ограничитель. Чайка принимает решения, но реализовывать их приходится команде. Если чайка сам пытался написать что-то серьёзное, реальность довольно быстро ставила его на место "мордой об код". Это оставляло пространство для диалога менеджер-инженер.
Нейросеть эту обратную связь убрала. Теперь чайка может насрать прямо в кодовую базу, минуя команду, и результат будет выглядеть профессионально. Код собирается. Фича работает. Задача закрыта быстрее, чем закрыл бы её любой из подчинённых. Всё, что видит сам чайка, подтверждает его представление о себе: он опытный, он быстрый, у него «всё рабочее».
Чайка-менеджер получил иллюзию собственной компетентности, подкреплённую работающим кодом. Раньше она держалась на должности и самомнении, теперь у неё есть «доказательства».
А вот последствия никуда не делись. Они просто сдвинулись во времени. Через месяц, через три, через полгода мины начнут срабатывать. Разбираться с ними будут те, кто понимает код, а не тот, кто его заказал. В споре о причинах у чайки будет железный аргумент: «У меня всё работало, это вы что-то трогали». А у разработчиков — только объяснение, почему два параллельных пути урона изначально были плохой идеей. Объяснение против работающей фичи и скорости закрытия задач — неравный бой, особенно когда объясняющий ниже по должности.
До эпохи ИИ у чайкиных продуктов жизнедеятельности был хотя бы фильтр в лице команды. Теперь команда нужна только для разгребания.
Вместо заключения
«Никогда такого не было — и вот опять». Каждая новая технология обещает, что теперь качество станет доступно всем. Высокоуровневые языки, фреймворки, Stack Overflow, low-code, теперь нейросети. И каждый раз качество остаётся у тех, у кого оно было, а остальные получают способ производить больше того же самого, только быстрее.
Отличие нынешнего витка в одном: он рушит цепочку воспроизведения кадров... но про это я уже писал.
Ну и финальный вывод: для тех, кто разбирается в программировании, нейросеть — верный помощник, который усиливает оператора: проверяет, подсказывает, находит то, что пропустил глаз. Для жопоруких говноделов это насос на трубе с фекальными массами. Он помогает загнать их поглубже и спрятать получше, но фундамент от этого крепче не становится.
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.