Субпрайм-кризис кода. Долгосрочная цена ИИ-ассистентов

- -
- 100%
- +
Введите правило: «Не мержить код, который не можешь объяснить на пальцах». Не на языке кода. На пальцах. Человеку, который не знает контекста. Если не получается — не мержить.
Начните ADR. Один файл на одно решение. Дата. Контекст. Альтернативы. Почему так. Не для всего. Для того, что может удивить через год.
Назначьте owner для модуля. Человек. Не команда. Он не обязан всё знать. Он обязан отвечать.
Метрики
Метрика Формула Что показывает
Unexplainable PR ratio PR без объяснения / всего PR Потеря замысла
Time to context Время на понимание модуля Глубина чёрного ящика
ADR coverage Модулей с ADR / всего модулей Восстановленный замысел
Log coverage Функций с логированием / всего функций Готовность к отладке
Runbook coverage Модулей с runbook / критичных модулей Готовность к инциденту
Формула black box ratio (оценка):
text
Black Box Ratio = (Unexplainable modules / Всего модулей) × 100%
Тимлид посчитал: из двадцати пяти модулей пять он не может объяснить. Двадцать процентов. Если больше — вы работаете не с системой, а с набором мин.
Кейс из trenches
Собирательный кейс. Детали изменены.
DevTools-компания, 30 инженеров, платформа для аналитики кода. Стек: TypeScript + Node. js + Rust + ClickHouse. Внедрили ИИ-ассистента год назад. К концу года 60% кода написано ИИ.
В январе новый разработчик получил задачу: добавить поддержку нового языка в парсер. Он открыл модуль LanguageDetector. 800 строк на Rust. Сгенерировано. Комментариев — три. Тестов — ноль. git blame — «AI assistant».
Он попросил помощь. Тимлид сказал: «Я тоже не знаю, как оно работает». Архитектор сказал: «Мы его не трогали год, потому что работает». Через два дня разработчик сдался и написал свой детектор — 200 строк. Работал хуже, но был понятен.
Через месяц два детектора сосуществовали. Через два — прод падал три раза, потому что они давали разные результаты для одного языка. Через три — команда решила: оставить один. Какой? Никто не знал, какой из них правильный. У сгенерированного не было модели. У нового — была, но хуже.
Тимлид сел за археологию. Восемь часов, три инженера. Разобрали LanguageDetector по функциям. Написали контракты для трёх ключевых. Написали 40 тестов на поведение. Задокументировали три ADR: почему такой алгоритм, почему такая структура данных, почему такие edge cases. Удалили второй детектор. Назначили owner.
Через полгода сгенерированный детектор работал. Не потому что стал понятнее. А потому что у него появилась модель — контракты, тесты, ADR. Модель, которую не дал ИИ. Модель, которую построила команда.
Стоимость археологии: 24 человеко-часа (оценка). Стоимость rewrite, если бы пошли этим путём: 200+ человеко-часов. Разница — в понимании.
Красные флаги
«Работает — не трогай.»
«Разберёмся потом.»
«Нам не нужно понимать, нам нужно доставлять.»
«Комментарии потом добавим.»
«Тесты — это для слабых команд.»
«У нас нет времени на археологию.»
«ИИ знает, что делает.»
Антипаттерн
Как делать НЕ надо:
Мержить код без объяснения.
Писать комментарии, описывающие «что», а не «почему».
Полагаться на документацию, которая устаревает.
Не добавлять логирование в критичные модули.
Не писать runbook.
Оставлять модуль без owner-а.
Верить, что «работает» = «можно поддерживать».
Делать rewrite, не попробовав археологию.
Чеклист
□ Проведена археология хотя бы одного модуля.
□ Написаны контракты для критичных функций.
□ Проверено логирование в критичных модулях.
□ Введено правило: «Не мержить код, который не можешь объяснить на пальцах».
□ Начаты ADR.
□ Назначен owner для каждого критичного модуля.
□ Есть runbook для модулей, которые падают в 3 ночи.
□ Black Box Ratio известен и отслеживается.
□ Команда знает разницу между «работает» и «понятно, почему работает».
□ Никто не говорит «ИИ знает, что делает».
Что запомнить
Код, который работает, но непонятен, — это не актив. Это мина с часовым механизмом. Взрывается не тогда, когда сломался. Взрывается тогда, когда кто-то попробовал изменить.
Что почитать
«Working Effectively with Legacy Code» (Michael Feathers) — как разбирать код без автора. Прямое руководство к археологии.
«Refactoring» (Martin Fowler) — как менять код, не ломая поведение. Но сначала — понять поведение. Без этого рефакторинг — это переписывание.
«Domain-Driven Design» (Eric Evans) — про то, как строить модель, а не состояние.
«Documenting Software Architectures» (Clements et al.) — про ADR и архитектурные решения.
«The Pragmatic Programmer» (Hunt, Thomas) — про разницу между «работает» и «правильно».
Глава 7. Парадокс review: генерация быстрее мышления
«Я не ревьюю PR. Я ставлю печать. Печать называется „LGTM“.»
— из треда на DOU про review-усталость
Тезис
ИИ пишет 500 строк, человек ревьюит 500 строк. Ревью становится узким местом — и его начинают имитировать. Review latency падает, defect escape rate растёт. Это не лень ревьюера. Это математика: если у вас 15 PR в день и 8 часов, у вас 32 минуты на PR. Включая чтение, понимание, проверку тестов, обсуждение. 32 минуты на 700 строк — это не ревью. Это имитация. И она опаснее, чем отсутствие ревью вообще, потому что создаёт иллюзию контроля.
Ключевые вопросы
Почему review не масштабируется вместе с генерацией?
Что такое rubber-stamping и как его распознать?
Почему «выглядит нормально» — это когнитивная ловушка?
Как ревьюить быстрее, не теряя качество?
Что такое risk-based review и почему он работает?
7.1. Асимметрия: генерация vs ревью
Разработчик открывает чат с ИИ. Пишет промпт: «Добавь поддержку вебхуков в модуль уведомлений». Через 30 секунд — 600 строк. Он просматривает. Выглядит логично. Мержит.
Ревьюер открывает этот PR. 600 строк. Незнакомый модуль. Сгенерировано. Он читает первую функцию. Понимает. Вторую. Понимает. Третью — начинает терять нить. Четвёртую — пропускает. К десятой он смотрит на часы: 12 минут прошло. У него ещё 14 PR в очереди. Он пишет «LGTM» и закрывает.
Генерация занимает 30 секунд. Ревью — 30 минут. Если бы ревьюер читал внимательно. Он не читал. Он ставил печать.
Вот асимметрия. ИИ ускорил генерацию в 100 раз. Ревью не ускорилось вообще. Оно осталось линейным: чем больше строк, тем больше времени. Если раньше разработчик писал 100 строк в день и ревьюер тратил на них 15 минут, то теперь разработчик генерирует 1500 строк, а ревьюер должен тратить 4 часа. У него нет 4 часов. У него 8 часов на всё.
Что происходит? Ревьюер делает то, что делает любой человек под давлением объёма: он сокращает. Сначала сокращает чтение. Потом сокращает понимание. Потом сокращает проверку тестов. Остаётся ритуал: открыть PR, пролистать, поставить галочку, закрыть.
Это не халатность. Это адаптация. Система требует от ревьюера невозможного — и он находит способ выжить. Способ называется rubber-stamping.
7.2. Rubber-stamping: как выглядит имитация review
Ревьюер открывает двенадцатый PR за день. 700 строк. Он листает. Функции выглядят знакомо. Тесты зелёные. Он пишет «LGTM» и идёт домой. В понедельник он не вспомнит этот PR. Через месяц — тем более.
Это rubber-stamping. Формально ревью было. Фактически — нет.
Как распознать rubber-stamping в своей команде:
Review latency меньше 5 минут на PR. Если PR из 500 строк ревьюится за 3 минуты, его не читали.
Review depth — меньше 1 минуты на 100 строк. Если это число падает, ревью умирает.
LGTM rate — больше 90%. Если почти все PR получают «LGTM» без комментариев, ревьюеры не вовлечены.
Нет комментариев, кроме «nit». Если единственные комментарии — про пробелы и запятые, значит, логику не смотрели.
Никто не просит изменения. Если за неделю ни один PR не был отправлен на доработку, ревью — ритуал.
Rubber-stamping опасен не тем, что пропускает баги. Он опасен тем, что создаёт иллюзию контроля. Команда думает: «У нас есть ревью». Руководство думает: «У нас есть процесс». А в реальности — печать. Печать называется «LGTM». Она не читает код. Она его штампует.
Через месяц баги уходят в прод. Команда удивляется: «Как мы это пропустили?» Пропустили, потому что не читали. Через два — инцидент. Через три — postmortem. В postmortem напишут: «Недостаточное покрытие тестами». Не напишут: «Ревьюер не читал, потому что у него было 32 минуты на 700 строк».
7.3. Психология: «выглядит нормально» как когнитивная ловушка
Четверг, 11:15. Ревьюер открывает PR. 400 строк. Сгенерировано. Он читает первую функцию. Всё логично. Вторую. Тоже. Третью. Он ловит себя на мысли: «Выглядит нормально». Четвёртую пропускает. Пятую — по диагонали. К десятой он уверен: код хороший. Он мержит.
Через неделю — баг. В той самой функции, которую он пропустил. Он открывает код. Смотрит. Понимает: баг был очевиден. Если бы он читал.
«Выглядит нормально» — это не оценка. Это когнитивная ловушка. Мозг экономит энергию: когда первые несколько элементов паттерна совпадают с ожиданием, он перестаёт проверять остальные. Это называется «эффект первого впечатления». Работает в жизни. Не работает в ревью.
ИИ усиливает ловушку. Сгенерированный код стилистически однороден. Он выглядит «правильно». Отступы, нейминг, структура — всё как в учебнике. Мозг ревьюера расслабляется: «Это не спагетти, это нормальный код». И перестаёт искать логические ошибки. А они там есть — просто выглядят аккуратно.
Вот почему сгенерированный код опаснее рукописного. Рукописный код часто выглядит плохо — и это сигнал: «Смотри внимательно». Сгенерированный код выглядит хорошо — и это антисигнал: «Всё в порядке, можно не смотреть». А внутри — мина.
Что делать с ловушкой:
Читайте не подряд, а выборочно. Начните с конца. Потом с середины. Потом с начала. Мозг не успеет расслабиться.
Ищите не «что не так», а «что здесь может быть не так». Не оценивайте. Предсказывайте. Где здесь может быть баг? Где здесь может быть неочевидная связь?
Задавайте вопросы вслух. Даже если некому. «Почему здесь reduce? Почему не map? Почему здесь try/catch, а не throw?» Если ответа нет — это красный флаг.
Не ревьюйте больше 300 строк за раз. После 300 строк внимание падает на 40% (оценка). Разбейте PR или отложите.
«Выглядит нормально» — это не ревью. Это капитуляция.
7.4. Уровни риска: не всё ревьюить одинаково
Тимлид смотрит на очередь PR. 18 штук. Половина — сгенерированный boilerplate. Четверть — тесты. Четверть — бизнес-логика. Он понимает: если ревьюить всё одинаково, не хватит времени ни на что.
Он вводит уровни риска.
Уровень Что входит Кто ревьюит Сколько времени
Критичный Платежи, auth, персональные данные, архитектура Минимум 2 ревьюера 30—60 минут
Средний Бизнес-логика, интеграции, API 1 ревьюер 15—30 минут
Низкий Boilerplate, тесты, документация, стиль Автомерж или 1 быстрый ревьюер 0—5 минут
Критичный PR не мержится без двух ревьюеров. Один — автор, второй — независимый. Оба должны понимать, что делает код. Если не понимают — PR не мержится. Точка.
Средний PR ревьюит один человек. Но он обязан прочитать. Не пролистать. Прочитать. Если PR больше 300 строк — разбить или отложить.
Низкий PR — автомерж. Тесты, boilerplate, документация. Если CI зелёный — мержим. Если CI красный — не мержим. Никто не тратит на это время.
Risk-based review не ускоряет ревью вообще. Он ускоряет ревью критичного. Потому что освобождает время от низкого. Если вы тратите 30 минут на boilerplate, у вас нет 30 минут на платежи. Если вы тратите 2 минуты на boilerplate, у вас есть 28 минут на платежи.
Что меняется:
Review latency для критичных PR — растёт. Это нормально. Лучше 2 часа на платёж, чем 5 минут.
Review latency для низких PR — падает до нуля. Автомерж.
Defect escape rate — падает. Потому что критичное ревьюится внимательно.
Review-усталость — падает. Потому что ревьюер не тратит силы на ерунду.
Главное правило: критичный PR не мержится, пока хотя бы один ревьюер не может объяснить, что делает код. Не «выглядит нормально». А «я понимаю, что здесь происходит, и могу объяснить».
7.5. AI-aware review: чеклист и запрет на слепой merge
Тимлид пишет чеклист. Не для всех PR. Для критичных. Он вешает его в шаблон PR. Теперь каждый критичный PR проходит через семь вопросов.
Чеклист критичного PR:
Понимание. Можешь объяснить, что делает код? Если нет — не мержить.
Безопасность. Проверены ли входные данные? Нет ли секретов в коде? Нет ли уязвимых зависимостей?
Зависимости. Все ли импорты существуют? Все ли они лицензионно чистые? Нет ли галлюцинированных пакетов?
Тесты. Есть ли тесты на поведение? Покрывают ли они edge cases? Не сгенерированы ли тесты тем же ИИ, что и код?
Обработка ошибок. Что происходит при ошибке? Логируется ли? Есть ли retry? Есть ли fallback?
Логирование и метрики. Есть ли лог на входе и выходе? Есть ли метрика для мониторинга?
Стоимость. Не создаёт ли код непредсказуемых облачных расходов? Нет ли бесконечных циклов? Нет ли N+1 запросов?
Если хотя бы один пункт — «нет», PR не мержится. Не «доработаем потом». Не мержится.
Запрет на слепой merge — это не бюрократия. Это единственный способ не превратить ревью в ритуал. Если вы разрешаете merge без проверки, вы разрешаете foreclosure. Рано или поздно.
AI-aware review отличается от обычного одним: вы предполагаете, что код может быть сгенерирован. Не «автор написал и подумал». А «модель сгенерировала и не подумала». Значит, проверка должна быть жёстче. Не потому что автор плохой. Потому что у автора не было замысла — был промпт.
Практика: что сделать завтра
Посчитайте review latency за последнюю неделю. Среднее время от открытия PR до merge. Если меньше 10 минут на PR из 300+ строк — вы в зоне rubber-stamping.
Введите уровни риска. Критичный, средний, низкий. С разным временем и числом ревьюеров. Начните с критичного. Остальное — потом.
Повесьте чеклист в шаблон PR. Семь вопросов. Для критичных PR — обязательны. Если хотя бы один «нет» — не мержить.
Введите правило: «Не мержить код, который не можешь объяснить». Не «выглядит нормально». А «я понимаю, что здесь происходит». Если не понимаешь — не мержить. Точка.
Проверьте LGTM rate. Если больше 90% PR получают LGTM без комментариев — ревью умирает. Введите обязательный комментарий для критичных PR: что проверено, что нет.
Разбейте большие PR. Если PR больше 300 строк — разбить. Если нельзя разбить — отложить и ревьюить утром, на свежую голову.
Метрики
Метрика Формула Что показывает
Review latency Время от PR до merge Скорость ревью
Review depth Время на ревью / LOC Глубина ревью
LGTM rate LGTM без комментариев / всего PR Rubber-stamping
Defect escape rate Баги в проде / всего багов Качество ревью
Rework rate Переписанный код / всего кода Пропущенные проблемы
Review capacity Доступное время / объём PR Перегрузка ревьюера
Формула review capacity (оценка):
text
Review Capacity = (Доступное время на ревью / Объём PR) × 100%
Если capacity <50% — ревьюер не успевает. Если <20% — ревью имитируется. Если <10% — ревью нет.
Кейс из trenches
Собирательный кейс. Детали изменены.
Финтех-стартап, 40 инженеров, платформа для платежей. Стек: Ruby on Rails + Go + PostgreSQL + Kafka. Внедрили ИИ-ассистента в марте. К июню velocity выросла на 150%. Дашборд зелёный. Руководство довольно.
В июле тимлид заметил: review latency упала до 8 минут на PR. Раньше было 45 минут. Он посмотрел на PR. 600 строк. 8 минут. Он понял: ревью умерло.
Он провёл аудит. LGTM rate — 94%. Review depth — 0.8 минуты на 100 строк. Defect escape rate вырос в три раза за квартал. Три инцидента в проде, все — из PR, которые получили «LGTM» без комментариев.
Он собрал команду. Показал цифры. Никто не удивился. Один ревьюер сказал: «У меня 15 PR в день. Я не могу читать. Я могу только штамповать».
Что сделали:
Ввели уровни риска. Критичный, средний, низкий.
Критичный PR — минимум 2 ревьюера, 30—60 минут, обязательный чеклист.
Средний — 1 ревьюер, 15—30 минут.
Низкий — автомерж, если CI зелёный.
Ввели правило: «Не мержить код, который не можешь объяснить».
Повесили чеклист из семи вопросов в шаблон PR.
Разбили большие PR. Если больше 300 строк — либо разбить, либо отложить.
Через три месяца:
Review latency для критичных PR: 2 часа. Для низких: 0.
LGTM rate: 60% (для низких — норма, для критичных — нет).
Review depth: 4 минуты на 100 строк (для критичных — 8).
Defect escape rate: упал в 2,5 раза (оценка).
Velocity: упала на 20%. Дашборд перестал быть зелёным.
CEO спросил: «Почему velocity упала?» Тимлид ответил: «Потому что мы начали читать код». CEO кивнул. Больше не спрашивал.
Конец ознакомительного фрагмента.
Текст предоставлен ООО «Литрес».
Прочитайте эту книгу целиком, купив полную легальную версию на Литрес.
Безопасно оплатить книгу можно банковской картой Visa, MasterCard, Maestro, со счета мобильного телефона, с платежного терминала, в салоне МТС или Связной, через PayPal, WebMoney, Яндекс.Деньги, QIWI Кошелек, бонусными картами или другим удобным Вам способом.



