JavaRush /Курси /Claude code /Виправлення помилок і regression evidence

Виправлення помилок і 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[Запустили прицільні перевірки]
    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 перестав помилятися», а поведінку зафіксовано як перевірювану й захищено від відкату.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ