Баг вызывает почти рефлекторное желание - сразу его чинить. Есть симптом, есть подозрение, есть агент, готовый писать код: соблазн проскочить от жалобы к правке минуя всё остальное велик. Именно этот прыжок и порождает вторую волну багов - когда чинят не причину, а то, что на неё похоже, и симптом уходит, чтобы вернуться сбоку.
Наивный ход понятен: правка ощущается как прогресс, а диагностика - как задержка. Кажется, что опытный агент "и так видит", в чём дело, и просить его воспроизводить проблему - лишний шаг. За этим стоит вера, что причина очевидна из симптома. Но симптом чаще всего совместим с несколькими причинами, и выбор между ними на глаз - это ставка, а не диагноз.
Ломается это на первом же шаге, который принято пропускать, - на воспроизведении. Пока баг не воспроизведён стабильно и не зафиксировано окружение, в котором он проявляется, нет и предмета работы: любая правка проверяется по ощущению "вроде больше не повторяется". Стабильный reproduction - это не бюрократия, а превращение расплывчатой жалобы в наблюдаемый факт, который можно опровергнуть или подтвердить. Заодно фиксация окружения отсекает целый класс ложных следов: баг, живущий только на определённой версии, наборе данных или конфигурации, перестаёт выглядеть случайным, как только условия его появления названы явно.
Отсюда порядок отладки, обратный наивному. Сначала зафиксировать минимальный reproduction и environment. Затем попросить агента назвать две-три гипотезы о причине и, что важнее, для каждой - наблюдение, которое её опровергнет. Дальше добавить диагностику или узкий тест, не трогая production-код. Менять код - только после того, как причина подтверждена. И в конце убрать временный logging и оставить regression test, который зафиксирует, что баг больше не вернётся незаметно.
Центр этого порядка - гипотезы с опровергающими наблюдениями, и это не формальность. Требование назвать, что именно опровергнет догадку, отсекает подтверждающее мышление: агент перестаёт искать доводы в пользу первой версии и начинает её проверять. Хорошая гипотеза - та, которую можно убить наблюдением; если ни одно наблюдение её не опровергает, это не гипотеза, а вера, и код по ней менять рано.
Этот порядок удобно закрепить коротким постоянным контрактом для агента, чтобы не проговаривать его каждый раз. Он умещается в несколько строк: воспроизвести без изменения production-кода, показать причинную цепочку от входа до сбоя, для каждой гипотезы назвать опровергающее наблюдение и лишь затем предложить минимальный patch и regression test. Такой контракт держит отладку в правильной последовательности даже под давлением "быстрее бы почвинить".
Цена нарушенного порядка асимметрична и коварна. Правка до подтверждения причины иногда "срабатывает" - симптом исчезает, - и это худший исход, чем явная неудача: причина осталась, а сигнал о ней вы только что заглушили. Баг вернётся в другом месте, под другим симптомом, и связать его с этой правкой будет уже трудно. Время, сэкономленное на диагностике, возвращается процентами в виде плавающего дефекта. Особенно это бьёт по автономной работе: агент, которому позволили чинить симптом, уверенно закроет задачу как выполненную, а цепочка последствий всплывёт уже без него и без контекста, в котором её было легко распутать.
Это не значит, что каждую опечатку нужно вести через полный протокол. Для дефекта с очевидной и единственной причиной воспроизведение и правка сходятся в один шаг. Протокол окупается там, где симптом неустойчив, причин может быть несколько или баг плавает между запусками: чем менее очевидна причина, тем дороже угадывать и тем дешевле сначала воспроизвести.
Проверять стоит не только исчезновение симптома, но и качество доказательства причины. Правильная проверка - regression test, который до правки падал, а после проходит: он показывает, что вы адресовали именно причину, а не замаскировали симптом. Если такого теста построить не удаётся, причина, скорее всего, не подтверждена - вы наблюдаете совпадение, а не установленную связь, и радоваться зелёному экрану рано. Полезный контрольный вопрос перед закрытием: могу ли я одной фразой назвать причину и показать наблюдение, которое её подтвердило. Нет такой фразы - значит, вы видели исчезновение симптома, а не устранение причины.
Типичные провалы повторяют пропущенные шаги. Нет воспроизведения - чинят по описанию и мимо. Нет опровергающих наблюдений - цепляются за первую гипотезу и подтверждают её сами себе. Нет regression test - тот же баг возвращается через месяц как новый. Признак у всех один: код изменили раньше, чем причину назвали и подтвердили. Сначала воспроизведение и причина - потом правка; иначе вы лечите не болезнь, а её тень.
Воспроизведи ошибку без изменения production-кода.
Покажи причинную цепочку от входа до сбоя.
Для каждой гипотезы назови наблюдение, которое её опровергнет.
После подтверждения предложи минимальный patch и regression test.