JavaRush /Курсы /Claude code /Bug fixing и regression evidence

Bug fixing и regression evidence

Claude code
18 уровень , 2 лекция
Открыта

1. «Не вижу ошибку» — это ещё не «починил»

Когда вы только начинаете работать с кодом, очень легко спутать две разные вещи: «ошибка больше не всплывает» и «дефект исправлен». С AI их особенно легко перепутать. Claude умеет убедительно маскировать проблему: поставит null-проверку не там, слишком широко перехватит исключение, вернёт красивое сообщение. Ощущение победы есть, причина осталась. Баг просто переоделся и ждёт следующего релиза.

Проще говоря, regression test — маленькая проверка, которая ловит именно этот баг, чтобы он не вернулся после очередного «небольшого улучшения». Regression evidence — доказательства, что вы закрепили исправление как проверяемое поведение, а не заглушили симптом. Яркая метка на трещине в стене: поползёт снова — увидите сразу.

В Commerce OS это хорошо видно. Отправили руками пустую корзину, увидели 400 — мало. Через две недели кто-то поменяет валидацию заказа, не зная о старом инциденте, и система снова начнёт отвечать 500. С regression-тестом откат случится не тихо, а с красной лампочкой. А красная лампочка полезнее оптимизма.

2. Bugfix-loop: дисциплинированный цикл

Когда баг уже понят и root cause найден, очень хочется ускориться: «Claude, всё, просто почини». Но багфикс — не импровизация. Гораздо надёжнее воспринимать его как короткий цикл, где каждый шаг даёт конкретный артефакт: такой цикл легче проверить, объяснить и положить в PR.

flowchart TD
    A[Воспроизвели баг] --> B[Зафиксировали root cause]
    B --> C[Написали падающий regression test]
    C --> D[Сделали минимальный fix]
    D --> E[Прогнали targeted checks]
    E --> F[Проверили соседние проверки]
    F --> G[Прочитали diff]
    G --> H[Собрали regression evidence]

Ниже удобно держать перед глазами простую карту этого цикла:

Шаг Что вы делаете Что получаете на выходе
1 Повторяете баг Понятный сценарий воспроизведения
2 Формулируете root cause Короткое утверждение о причине дефекта
3 Пишете regression-тест до фикса Падающую проверку, которая ловит баг
4 Вносите минимальный fix Небольшой и объяснимый diff
5 Запускаете targeted checks Подтверждение, что конкретный дефект исправлен
6 Запускаете соседние проверки Базовую уверенность, что рядом ничего не сломали
7 Читаете diff глазами Контроль scope и отсутствие лишних изменений
8 Фиксируете evidence Материал для EVIDENCE_LOG.md и PR walkthrough

Обратите внимание на одну тонкость. В этом цикле тест идёт до фикса, а не после. Поменяете код, а потом напишете тест под уже изменённое поведение — вы не доказали, что тест ловит старый баг. Только что новый код согласован сам с собой. А согласованность бывает и у ошибки. Код вообще очень терпим к чужим заблуждениям.

3. Сначала падающий regression test

На этом шаге обычно происходит маленькая внутренняя борьба: вам уже очень хочется чинить код, а я будто специально прошу сначала тест. Пока он не падает на старом поведении, у вас нет точки отсчёта — а без неё исправление легко перепутать с самовнушением.

Раз баг проявлялся на уровне HTTP-ответа, то и тест здесь смотрит на HTTP. Разбираться в тестовой инфраструктуре Spring сейчас не нужно. Держите одну мысль: mockMvc — маленький встроенный клиент, отправляющий запрос в приложение без реального браузера.

import org.junit.jupiter.api.Test;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@Test
void rejectsEmptyCart() throws Exception {
    mockMvc.perform(post("/api/orders")
            .contentType("application/json")
            .content("{\"items\":[]}"))
        .andExpect(status().isBadRequest()); // до фикса: ожидали 400, получили 500
}

Если вы запускаете только этот тест до исправления, он и должен упасть. Именно этого мы и хотим. Падающий тест тут не проблема, а доказательство, что вы поймали нужный баг. Стал бы сразу зелёным — значит, он вообще не проверяет этот дефект. Локальный запуск:

./gradlew :orders:test --tests OrderControllerTest.rejectsEmptyCart
# До фикса тест падает: endpoint возвращает 500 вместо ожидаемого 400

С Claude Code здесь полезно работать буквально. Не «исправь баг» — сначала только тест, потом production-код:

Сначала напиши regression test для бага с пустой корзиной.
Не меняй production-код.
Покажи, что тест падает на текущем поведении, и коротко объясни, почему это связано с найденной root cause.

Такой порядок дисциплинирует и вас, и AI. Claude перестаёт быть торопливым чинителем всего на свете и становится аккуратным помощником в одном шаге.

4. Минимальный fix: чиним причину, а не магазин

После падающего regression-теста появляется очень приятное чувство: теперь можно исправлять с понятной целью. И именно на этом месте AI любит устроить маленький праздник инициативы: вместо локального fix заодно вынесет общую валидацию, унифицирует обработку исключений, переименует DTO, причешет архитектуру. Красиво — и для багфиксового PR почти всегда вредно.

Минимальный fix бьёт ровно по причине и почти не трогает остальную систему. В нашем случае пустая корзина проходит слишком далеко — отловим её раньше и вернём корректный 400.

import org.springframework.http.HttpStatus;
import org.springframework.web.server.ResponseStatusException;

if (request.items() == null || request.items().isEmpty()) {
    throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Корзина пуста");
}
return checkoutService.checkout(request.items());

Этот кусок кода хорош не гениальностью, а тем, что его легко объяснить: не меняет контракт успешного заказа, не трогает другие endpoint’ы, не тащит зависимости, не превращает багфикс в «архитектурное переосмысление чекаута». Иногда лучший багфикс — скучный багфикс. Скуку тут сильно недооценивают.

Если хотите направить Claude в нужное русло, формулируйте задачу очень жёстко:

Сделай минимальный fix только для бага с пустой корзиной.
Не меняй другие endpoint'ы.
Не выноси общую валидацию в новые классы.
Не добавляй зависимости.
После изменения покажи diff и запусти только связанные проверки.

Заметьте: мы не просим «сделай красивее». В bugfix-задачах «красивее» — приглашение к scope creep: одна строчка проверки обрастает новым классом валидации, унификацией исключений и переименованием DTO, и багфикс тихо превращается в рефакторинг чекаута.

5. Одного зелёного теста мало

Когда regression-тест стал зелёным, возникает следующий соблазн: решить, что работа кончена. Один зелёный тест — хорошо, но всё ещё не вся картина. Ревьюер, тимлид или вы сами через неделю захотите понять не только «что стало зелёным», но и «почему этому можно верить». Нужен пакет доказательств — несколько независимых признаков, что исправление стоит принять.

Для Commerce OS такой bundle удобно представить так:

Доказательство Что оно подтверждает Наш пример
Падающий до фикса тест Баг реально существовал
rejectsEmptyCart()
падал на 500
Тот же тест после фикса Конкретный дефект исправлен После правки тест ждёт 400 и зелёный
Ручная проверка сценария Пользователь видит правильное поведение Пустая корзина больше не валит заказ
Соседние проверки зелёные Рядом ничего очевидно не сломали Проверки модуля orders проходят
Маленький diff Не было лишнего scope Изменены только controller и test
Root cause note Fix связан с причиной, а не с симптомом Ранняя валидация пустой корзины

Ручную проверку можно сделать очень просто. Даже если вы не фанат консоли, здесь полезно один раз увидеть поведение глазами HTTP:

curl -i -X POST http://localhost:8080/api/orders \
  -H "Content-Type: application/json" \
  -d '{"items":[]}'
# После фикса ожидаем: HTTP/1.1 400 Bad Request

Если у вас теперь есть и зелёный тест, и корректный ручной 400, и маленький diff, картина становится намного убедительнее. Это уже не «ну вроде заработало», а «вот доказательства, что мы поймали старый дефект и починили без попутного хаоса».

На практике такие заметки удобно сначала держать в EVIDENCE_LOG.md, а потом переносить краткую версию в PR walkthrough:

## Regression evidence

- До фикса тест `OrderControllerTest#rejectsEmptyCart` падал: endpoint отвечал 500
- После фикса тот же тест зелёный
- Ручная проверка `POST /api/orders` с `{"items":[]}` возвращает 400
- Дополнительные проверки модуля `orders` зелёные
- Diff ограничен двумя файлами: controller + test

Обратите внимание: никакой длинной повести. Evidence — коротко, проверяемо, по фактам. Ревьюер не должен гадать, где кончаются наблюдения и начинаются добрые намерения.

6. Claude Code в bugfix-loop без сокрытия

На этом этапе Claude Code особенно полезен — но только если не заставлять его играть все роли разом. Одна сессия пишет тест, пишет fix, а потом бодро уверяет, что всё идеально: самоуверенный коллега, который сам себе выдал премию. С AI это случается ещё чаще, чем с людьми — слишком уж он любит звучать убедительно.

Гораздо надёжнее разделить работу хотя бы на три роли.

Роль Claude Что вы у него просите Зачем это нужно
Автор regression-теста Написать тест и показать, что он падает Зафиксировать старое сломанное поведение
Автор минимального fix Изменить только нужный участок кода Удержать diff маленьким и понятным
Сборщик evidence Коротко перечислить, чем подтверждён багфикс Подготовить материал для EVIDENCE_LOG.md и PR

На практике это могут быть три коротких запроса, а не один длинный. Сначала:

Напиши regression test для бага с пустой корзиной.
Не меняй production-код.
Покажи, на чём именно тест падает.

Потом, после подтверждения теста:

Сделай минимальный fix под этот test.
Не трогай другие endpoint'ы и не делай рефакторинг.
После изменения запусти только связанные проверки.

И только в конце:

Собери краткий regression evidence:
какой test падал до фикса, что стало после фикса,
какие дополнительные проверки запущены,
какие файлы изменены.
Не оценивай качество решения, просто перечисли факты.

Последняя фраза здесь особенно важна: «не оценивай качество решения, просто перечисли факты». Она защищает от режима, где Claude не собирает evidence, а продаёт вам собственный код. Продавец из AI выходит иногда даже лучше, чем программист. Держите его подальше от кассы.

Падающий до фикса тест, минимальный diff, зелёный тест после исправления, короткий набор доказательств — и багфикс перестаёт быть хрупкой надеждой «вроде больше не всплывает». Regression test оставляет на дефекте яркую метку: поползёт снова — красная лампочка загорится сама, без вас. Именно этим закрытый баг отличается от заглушённого: не «AI перестал ошибаться», а поведение зафиксировано как проверяемое и защищено от отката.

1
Задача
Claude code, 18 уровень, 2 лекция
Недоступна
Regression test до фикса
Regression test до фикса
1
Задача
Claude code, 18 уровень, 2 лекция
Недоступна
Minimal fix по уже падающему regression test
Minimal fix по уже падающему regression test
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ