Почему ошибки в коде Windsurf проскакивают через ревью
Ревью человеком масштабируется ровно настолько, сколько строк человек способен удержать в голове. Широкая механическая правка ломает его вполне определённым образом: каждый фрагмент по отдельности правдоподобен, и внимание соскальзывает. Двенадцатый файл вы одобряете придирчивее первого, а дефект почти всегда оказывается в хвосте.
И дефект редко сидит в том, что изменилось. Он в том, что не изменилось. Переименование, пропустившее одно место вызова; новое обязательное поле, которое не передают три конструктора; обработчик, обновлённый везде, кроме ветки ошибки.
Три формы, в которых ломается широкая правка
Неполные замены
Одиннадцать мест вызова обновлены, одно пропущено — и оно на пути, по которому никто не ходит каждый день.
Типы часто этого не ловят, потому что пропущенное место и так было типизировано слабо.
Расхождение допущений
Два файла начинают по-разному понимать одно и то же значение — например, задана метка времени в секундах или в миллисекундах.
Каждый файл внутренне непротиворечив, поэтому по отдельности ничего подозрительного не видно.
Незаметное изменение поведения
Рефакторинг сохраняет интерфейс и тихо меняет порядок, значения по умолчанию или ветку обработки ошибок.
Тесты, написанные под старую реализацию, продолжают проходить, потому что проверяют интерфейс.
У всех трёх общее свойство: их находят, пользуясь приложением, а не читая его. Именно поэтому они так дорого обходятся.
Проверяйте поведение, а не дифф
Для широкой правки полезен не вопрос «корректно ли каждое изменение», а вопрос «делает ли продукт по-прежнему то, что делал, плюс новое». Это вопрос о поведении, и ответ на него получают, прогоняя развёрнутое приложение по сценариям, которых правка могла коснуться.
Чтобы покрытие приносило пользу, оно не обязано быть исчерпывающим. Правке, которая переименовывает поле по всему пути оформления заказа, достаточно одного сквозного прогона оформления заказа — и это ценнее, чем внимательное чтение двенадцати диффов.
Установите навык проверки прямо в редактор, чтобы агент запускал её сам, а не ждал вас.
Терминал
npm install -g @testsprite/testsprite-cli
testsprite setup
Панель управления выполняет ту же настройку, если вы предпочитаете ничего не устанавливать. Остальные возможности CLI описаны в репозитории CLI.
Отдавайте агенту сам сбой, а не свой пересказ
Когда что-то всё же ломается, цикл исправления замыкается только при пригодных свидетельствах. Что пытались сделать, что сделало приложение, где они разошлись. С этим агент вернётся в те же двенадцать файлов с конкретной целью. Со скриншотом он будет гадать — и вы снова начнёте всё описывать словами.
Как читать широкий дифф без самообмана
Честный способ отревьюить правку на двенадцать файлов существует, и он не в том, чтобы прочитать каждый фрагмент. Он в том, чтобы ещё до открытия диффа решить, что должно быть верно, чтобы эта правка была корректной, и проверить только это.
Для переименования это вопрос, переехали ли все места вызова — на него по большей части отвечает компилятор — и не собирает ли что-нибудь имя динамически, а на это он уже не отвечает. Для нового обязательного поля это каждый путь, где создаётся объект, включая тесты и фикстуры, которые молча продолжат передавать неполную версию. Для рефакторинга, сохраняющего интерфейс, это порядок операций и ветки ошибок, потому что именно их рефакторинг меняет незаметно.
Три вопроса на правку лучше, чем просмотр четырёхсот строк, и на выходе у вас остаётся честный список того, что всё ещё нужно проверить запуском.
Выбирайте сценарии по радиусу поражения, а не по важности
Инстинктивно покрывают самую важную функцию. Для широкой правки лучше спросить, каких сценариев она могла коснуться, и упорядочить их по тому, насколько незаметно они сломаются.
Платёжный сценарий важен, но за ним и следят. Если он сломается, вы узнаете об этом в течение часа. Опасны сценарии, которые значимы, но используются раз в неделю, а не раз в час: экспорт, приглашение, страница настроек, дайджест по расписанию. Именно там неполная замена спокойно живёт целый месяц.
Поэтому после широкой правки прогоняйте сначала тихие пути. Покрыть их дёшево, и больше их не проверяет никто.
Запускайте проверку на каждом пул-реквесте
Именно широкие правки хочется влить побыстрее, потому что дифф скучный. Это и есть аргумент за то, чтобы проверка была автоматической, а не держалась на чьей-то памяти.
Путей два, и выбор по сути сводится к тому, кто владеет пайплайном. Подключение GitHub App из панели управления не требует вообще никаких изменений в репозитории, потому что реагирует на деплой, который ваша сборка и так публикует. Добавление шага GitHub Actions помещает проверку в репозиторий, где её ревьюят так же, как остальную сборку.
Как проверить широкую правку, не читая её
TestSprite отвечает на вопрос, на который широкий дифф ответить не может: делает ли продукт по-прежнему то, что делал, плюс новое. Он открывает развёрнутое приложение, проходит сценарии, которых правка могла коснуться, и сообщает о поведении, а не о структуре.
Для этой задачи форма подходящая. Неполные замены, расходящиеся допущения и тихие изменения поведения одинаково невидимы в исходниках и очевидны в работе. Покрытие генерируется из вашего продукта, а не пишется вручную, поэтому тихие сценарии — экспорт, приглашение, страница настроек — попадают в него без того, чтобы кто-то о них вспоминал.
Когда что-то ломается, сбой возвращается единым пакетом, с которым ваш агент может работать, и здесь это важнее всего: указать ему на конкретное расхождение лучше, чем просить заново перебрать двенадцать файлов.
Почему типы этого не ловят?
Изрядную долю неполных замен они ловят, а вот расхождение допущений и незаметные изменения поведения — почти никогда, потому что и то и другое по построению корректно с точки зрения типов.
Может, просто просить о более мелких правках?
Это помогает — и отнимает значительную часть смысла использовать агентный редактор. Шаг проверки позволяет сохранить ширину и при этом знать, работает ли результат.
Работает ли это, если правка затрагивает только бэкенд?
Да, через покрытие на уровне API, а не через интерфейс. Принцип тот же: прогонять поведение, а не читать правку.
Сколько занимает такая проверка?
Достаточно долго, чтобы не запускать её на каждое нажатие клавиши, и достаточно быстро, чтобы она уложилась в пул-реквест. Там ей и место.
А если агент уже прогнал юнит-тесты?
Это полезно, но юнит-тесты обычно писались под те самые допущения, которые правка только что изменила. Они подтверждают внутреннюю непротиворечивость, а не внешнюю корректность.
На двенадцати файлах чтение диффа — это не проверка.
Ошибки в коде Windsurf прячутся в неполных заменах, расходящихся допущениях и тихих изменениях поведения. Проверяйте работающий продукт вместо диффа, возвращайте сбои агенту как свидетельства и сделайте так, чтобы проверка запускалась на каждом пул-реквесте.