Привет, Хабр!
Я фрилансер! ТГ-боты, бэкенды, AI-интеграции, с недавних пор embedded. В прошлой статье я показывал, как биржу залило вайб-кодерами и как просели цены на базовые проекты. Но там я говорил про цену. А есть вопрос следующий: а код-то у них рабочий? В смысле — рабочий по-настоящему, а не «вроде запускается».
Сразу дисклеймер: я каждый день работаю через Claude Code, я на них построил весь свой процесс. Именно поэтому мне интересно где у них системный предел.
И я внутри этого рынка, объективности не обещаю.
Вайб-кодер в чистой форме — человек, который вообще не имеет отношения к разработке — физически не способен оценить код. Для него работает = работает. А я утверждаю: код, сгенерированный нейронкой, всё равно будет более плоским, более ущербным и менее оптимальным, чем код живого разработчика. Проблема в том, как это доказать человеку, который код читать не умеет. Поэтому зайдём через аналогию, которую может проверить КАЖДЫЙ — через тексты.
Тексты от нейронки узнают уже все
Тут, я думаю, спорить никто не будет. Мы все научились чуять текст от нейросети. Эти бесконечные триколоны, «это не просто X — это Y», тяжёлые вводные конструкции, всё такое правильное, выверенное прямо по учебнику. Идеально и мёртво.
Да, хороший редактор с нейросетью сделает лучше. Но речь не о хорошем редакторе. Речь о человеке, который просит “напиши продающее описание” и принимает первый гладкий результат.
Откройте для себя мир кристально чистого звука с нашими беспроводными наушниками нового поколения. Это не просто наушники — это ваш персональный портал в безграничную вселенную музыки. Благодаря передовой технологии активного шумоподавления и эргономичному дизайну вы погрузитесь в любимые треки как никогда раньше. Идеальный выбор для тех, кто ценит качество, комфорт и стиль.
Текст грамматически безупречен и при этом абсолютно пустой. Он подходит под ЛЮБЫЕ наушники — не утверждает ничего проверяемого, не называет ни одной конкретной цифры, никого конкретного не представляет. Это водица, которую можно вылить на любой товар.
А что делает живой копирайтер? Он пишет конкретику, которую можно проверить: сколько часов держит зарядка, за сколько секунд коннектится, что слышно, а что нет. И — самое важное — он называет недостаток. Честный продающий текст почти всегда говорит, кому товар НЕ подойдёт, потому что это и есть доверие. Человек отказывается перехваливать, но нейронка не отказывает никогда.
В коде происходит ровно то же, просто это спрятано
Та же плоскость, те же повторяющиеся паттерны, одна и та же функция, переименованная пять раз. Тут всплывает асимметрия
В тексте посредственность видна демократически. Любой, кто много читал, носом чует нейронку. Плоскость текста легитимно замечают тысячи компетентных читателей.
В коде плоскость спрятана за зелёной галочкой тестов. Её видит ровно один человек — инженер, умеющий читать код. А вайб-кодер именно этого человека из процесса и убрал. То есть он купил не код, он купил видимость работающей системы и отдал наружу единственное суждение, которое сам выполнить не способен.
Я, кстати, уже писал про обратную сторону той же монеты — как Claude на неполном контексте уверенно «находил уязвимости», которых не было, потому что не видел систему целиком. Каждое его замечание было локально корректным и глобально неверным. Тут то же самое, только с другого конца: нейронка генерит локально корректный код, который глобально гнилой, а проверить это некому.
Примеры!
Методика такая. Сначала — настоящая функция из легендарного опенсорса, со ссылкой на файл, открывайте и проверяйте. Далее — то, что выдаёт НЕЙТРАЛЬНЫЙ чат с LLM на наивный промпт.
Важный момент, без которого эксперимент был бы жульничеством: промпт должен быть наивным — таким, какой реально способен сформулировать вайб-кодер, по конечной функции, без тайного знания о граничных случаях. Потому что — вот он, главный тезис — разрыв между кодом сеньора и кодом вайб-кодера, прогнанными через ОДИН И ТОТ ЖЕ LLM, это в основном разрыв в промпте. Несущие решения разработчика не попадают в промпт, потому что для него они очевидны, молчаливы, а вайб-кодер не может попросить то, о существовании чего не знает.
Три функции, специально расставленные по шкале видимости — от «это даже не-кодер увидит» до «это не видит почти никто».
1. secure_filename — видно глазами
Werkzeug, ядро Flask. Делает из имени файла безопасное имя для сохранения на диск.
def secure_filename(filename: str) -> str: filename = unicodedata.normalize("NFKD", filename) filename = filename.encode("ascii", "ignore").decode("ascii") for sep in os.sep, os.path.altsep: if sep: filename = filename.replace(sep, " ") filename = str(_filename_ascii_strip_re.sub("", "_".join(filename.split()))).strip("._") # на nt в каждой папке есть спец-файлы устройств — нельзя, чтобы файл # назывался так же, иначе подставляем подчёркивание if ( os.name == "nt" and filename and filename.split(".")[0].upper() in _windows_device_files ): filename = f"_{filename}" return filename
А вот что выдаёт нейтральный чат на «сделай безопасное имя файла»:
def secure_filename(filename): filename = os.path.basename(filename) filename = filename.replace(" ", "_") filename = "".join(c for c in filename if c.isalnum() or c in "._-") return filename
Работает? Да конечно же! Демка отвечает, имя чистится. А теперь смотрим, что наивная версия пропустила, причём это видно глазами:
secure_filename("..")— настоящая вернёт пустую строку, наивная вернёт.., то есть ссылка на родительскую папку выживает.secure_filename("CON.txt")на Windows — настоящая даст_CON.txt, наивная оставитCON.txt, зарезервированное имя устройства.secure_filename("naïve.txt")— настоящая приведёт к ASCII (naive.txt, портируется везде), наивная оставит не-ASCII, потому чтоisalnum()пропускает юникод-буквы.
Даже вайб-кодер тут способен увидеть: наивная версия оставляет .., зарезервированные имена Windows и Unicode-поведение, о котором автор промпта просто не подумал.
2.lru_cache— середина шкалы
CPython, стандартная библиотека. Кэширующий декоратор. Полная реализация длинная, поэтому покажу не весь файл, а характерный кусок из lrucache_wrapper.
Полную реализацию не привожу целиком: она слишком длинная для статьи, но ссылка выше. Важно не запомнить все детали CPython, а увидеть разницу.
cache = {} lock = RLock() # because linkedlist updates aren't threadsafe root = [] # root of the circular doubly linked list root[:] = [root, root, None, None] def wrapper(*args, **kwds): # Size limited caching that tracks accesses by recency nonlocal root, hits, misses, full key = make_key(args, kwds, typed) with lock: link = cache_get(key) if link is not None: # Move the link to the front of the circular queue link_prev, link_next, _key, result = link link_prev[NEXT] = link_next link_next[PREV] = link_prev last = root[PREV] last[NEXT] = root[PREV] = link link[PREV] = last link[NEXT] = root hits += 1 return result misses += 1
То есть настоящая реализация — это не просто словарик. Там есть ограничение размера, вытеснение давно не использованных значений, потокобезопасность и аккуратное построение ключа, которое не сводится к склейке строк.
А наивный ответ на «напиши простой аналог lru_cache»:
def lru_cache(func): cache = {} def wrapper(*args, **kwargs): key = str(args) + str(kwargs) if key not in cache: cache[key] = func(*args, **kwargs) return cache[key] return wrapper
Что не так:
Это вообще не LRU. Нет ограничения размера, нет вытеснения — кэш растёт бесконечно. Назвали
lru_cache, а получили утечку памяти.Хрупкий ключ.
str(args)+str(kwargs): разные объекты могут иметь одинаковое строковое представление, и тогда вернётся чужой закэшированный результат.Не потокобезопасно. Под нагрузкой — гонки.
Это уже не дыра, которую видно глазами. Это деградация, которую заметит разработчик на ревью или сервер на проде, когда память начнёт уползать. Вайб-кодер — нет.
3. constant_time_compare — не видит почти никто
Django. Сравнить токен пользователя с правильным, вернуть True/False. Настоящая (django/django, файл django/utils/crypto.py, BSD-3, © DSF) — буквально одна строка:
def constant_time_compare(val1, val2): return secrets.compare_digest(force_bytes(val1), force_bytes(val2))
А наивная, на тот же промпт:
def constant_time_compare(val1, val2): return val1 == val2
Работает идеально! Проходит все тесты и... уязвима. Обычное сравнение может иметь время выполнения, зависящее от совпадающего префикса и деталей представления данных — значит, время сравнения зависит от того, ГДЕ первое расхождение. Атакующий, замеряя время ответа, подбирает секрет посимвольно. Django сравнивает за постоянное время, чтобы этого нельзя было сделать.
Вот он, главный пример. Код корректен по поведению и эксплуатируем по сути. И заметить это можно, только если ты знаешь про timing-атаки. То есть существует целый слой, который вайб-кодер не видит в принципе. Не «проглядел», а физически не способен увидеть.
Это не случайные косяки, а паттерны
Почему так выходит?
Нейронка тянет самый частый ответ. Не лучший для вашей системы, не идиому вашей кодовой базы, не решение с учётом будущих изменений, а вероятный паттерн из обучения.
Она хорошо держит локальную связность и плохо держит глобальную. Отдельная функция выглядит нормально. Но в одном месте деньги уже
float, в другом копейки вint, в третьем строка"100.00"— и где-то на шве вылезает ошибка в сто раз. Прямой аналог хороших предложений в плохом эссе.Она редко пушбэчит. Помните наушники? Там то же самое. Нейронка не говорит: «это требование противоречиво», «так строить нельзя», «сначала надо решить модель данных». Сеньор иногда неприятен именно потому, что отказывает. Вайб-кодер теряет не просто исполнителя. Он теряет суждение о том, стоит ли вообще это делать.
Шкала видимости
Заметьте, как выстроились три примера. Шесть пальцев на руке у нейросетевой картинки видят все мгновенно. Плоский текст — внимательный читатель. Дыру в constant_time_compare — почти никто.
Чем «глубже» артефакт, тем невидимее в нём гниль и тем длиннее лаг до того момента, когда за неё прилетит счёт. И вот этот лаг вайб-кодера и обманывает: в секунду, когда «работает», он уверен, что поймал ценность.
Нейросети не обесценивают код
Они обесценивают наши способы не читать его. Раньше гладкий текст без ошибок был слабым, но всё-таки сигналом: кто-то думал, редактировал и далее - вкладывал усилие. Теперь нейронка печатает гладкость без мысли.
С кодом то же самое. Раньше «запускается», «компилируется», «тесты зелёные» означало: скорее всего, это прошёл человек, который понимает, что проверяет. Теперь эти прокси можно напечатать почти бесплатно.
И рынок постепенно это поймёт, текст уже прошёл этот порог: «слишком гладко и структурно» теперь часто читается как «нейронка». Код проходит его медленнее, потому что код мало кто умеет читать. Но ведь смысл тот же: «работает» всё меньше означает «надёжно». Не инфлирует только само обеспечение: понимание, вкус, способность отказать, умение держать систему целиком.
Я к этому уже приходил в методичке: AI — усилитель, а не уравнитель. Множитель, а не слагаемое. У эксперта он умножает экспертизу. У человека без знания он умножает незнание.
А способность отличить настоящую инженерию от уверенного бреда модели на обрывках контекста нельзя делегировать. В том числе самой модели.
В коде это работает так же, как в тексте. Просто в тексте вы это уже видите, а в коде — пока нет.
Поток сознания и мысли по таким ситуациям складываю в канал.
Комментарии (4)

OpenEducationOfficial
27.06.2026 10:40Прям в точку. У нас была похожая история — генерили через Claude эндпоинты для файлового хранилища, всё выглядело чисто, линтер доволен, тесты проходят. Потом senior посмотрел и нашёл именно path traversal — почти один в один как в статье. Самое обидное, что код был «уверенный», без единого намёка на то что там проблема.
С тех пор правило простое: LLM пишет черновик, человек с контекстом смотрит граничные случаи. Особенно всё что касается авторизации и файлов. Без этого — рулетка.

UnknownUser
27.06.2026 10:40Это всё не новость. Для того чтобы делать реально сложные проекты с помощью ЛЛМ надо разбираться в теме, архитектуре пприложений, особенностях стека итд.
Именно поэтому я уверен что разработчиков всех не поувольняют . Вернее, может какие нибудь ретивые оптимизаторы из менеджемента и поувольняют - но потом им придётся этих разработчиков нанимать обратно )).
Кстати, пример про копирайтинг с наушниками плохой. Говорю как меломан со стажем. И в донейросетевую эпоху рекламные тексты звукового оборудования все как один гарантировали кристально чистый звук - от дешманского оборудования за копейки до лампового хайенда собранного девственницами на склоне горы Фудзи. Так что тут мимо ).

okoloboga Автор
27.06.2026 10:40Эх, согласен про наушники, это скорее первое, что у меня было в поле внимания, поэтому про них и сделал)
BoCoder
Полезно, спасибо!