JavaRush /Курси /Claude code /Verification harness: сенсори

Verification harness: сенсори

Claude code
Рівень 20 , Лекція 0
Відкрита

1. Рефакторинг без harness — це лотерея

Під час refactor зовні ніби нічого не змінюється — і в цьому вся пастка. Користувач не скаржиться, кнопка не зʼявилася, а помилка вже тихо оселилася в коді. Саме тут інтуїція підводить найчастіше.

Уявіть, що ви лагодите електропроводку в квартирі. Можна, звісно, працювати за принципом «торкнувся дроту, наче не іскрить» — от тільки цей метод зазвичай подобається лише тим, кому потім не пояснювати, чому згоріла кухня. З кодом рівно та сама історія: перейменували метод, винесли логіку в helper, прибрали дублювання — і зламали прихований сценарій, якого не видно ні через інтерфейс, ні через diff.

Саме тут нам потрібен verification harness: набір локальних повторюваних перевірок зі зворотним звʼязком у процесі роботи. Ваша панель приладів: форматування поїхало, типи не збіглися, збірка розвалилася, тести більше не зелені, покриття просіло, а метод, який ви хотіли «безпечно» перейменувати, викликається в шести місцях — про два ви не здогадувалися.

Тут важливий ще один термін — deterministic sensor: перевірка, що на одному й тому самому коді дає один і той самий результат. Зелений, через хвилину без змін червоний? Не сенсор, а нестабільний тест. А «мені здається, код став чистішим» — узагалі не сенсор. Це настрій. У настрою в інженерії погана репутація.

Якщо говорити зовсім просто, нас зараз цікавить саме local verification harness — не командне «чи можна змерджити», а робота по кроках: чи не зламав я щось просто зараз маленькою зміною.

2. Із чого складається verification harness у проєкті

Коли чуєш слово harness, дуже хочеться уявити одну велику кнопку «перевірити все». На практиці все влаштовано прозаїчніше й корисніше: harness — це набір різних сенсорів: одні дешеві й швидкі, інші повільні, але важливі, треті працюють до зміни коду.

У Commerce OS цей harness не вигадується заново під кожну задачу. Він уже живе в проєкті й повʼязаний зі стеком: backend на Java і Spring Boot, frontend на Next.js, тести, збірка, лінтери, звіти покриття. Claude Code має його знайти й використати, а не влаштовувати революцію під гаслом «поставлю ще три інструменти». Революції не покращують верифікацію — лише роблять її довшою і злішою.

Нижче зручна карта основних сенсорів harness:

Сенсор Що він ловить Коли особливо корисний
Formatter розʼїханий стиль коду, зайві пробіли, хаотичне форматування майже після кожного редагування
Linter підозрілі конструкції, невикористані змінні, сумнівні патерни після маленького кроку й перед комітом
Type-check невідповідність типів, зламані сигнатури, помилки контрактів на рівні типів після зміни публічних методів, DTO, фронтенд-моделей
Build / assemble помилки збірки, зламані залежності модулів, проєкт, що не компілюється перед завершенням логічного кроку і перед PR
Unit tests локальна бізнес-логіка, маленькі шматки поведінки після точкової refactor-зміни
Integration tests взаємодію компонентів, сервісів, репозиторіїв, API-рівнів якщо зміна зачіпає кілька шарів
Coverage report сліпі зони, де зміна пройшла без адекватних перевірок перед PR, коли потрібно оцінити ризик
Static analysis глибші попередження щодо якості та надійності на ширшому прогоні
Light security scan очевидні небезпечні шаблони, якщо він уже вбудований у проєкт перед завершенням зміни
Code intelligence де використовується символ, хто кого викликає, яким є blast radius зміни до першого редагування

Останній рядок тут особливо важливий. Новачки часто сприймають harness як «те, що запускається після змін», але code intelligence працює не «після», а «до» — щоб ви не взялися рефакторити метод, який стирчить у трьох сервісах, двох тестах і одному внутрішньому API, про який забули.

Окремо зупинюся на coverage. Дуже легко почати думати так: раз є звіт, значить піднімати його треба будь-якою ціною. Ні. Coverage — не медаль, а термометр. За температури 39 ви не лікуєте термометр. Звіт потрібен, щоб побачити, де ви йдете майже без страховки.

3. Harness — це не quality gate

Локальний harness і quality gate — не одне й те саме, і на цьому місці студенти їх часто змішують. Механізми різні — і добре, що різні: інакше ви ганяли б повний CI на кожному збереженні файлу й швидко зненавиділи б і AI, і клавіатуру, і саму ідею працювати.

Дивіться на різницю так:

Локальний harness Quality gate
Дає автору швидкий зворотний звʼязок під час роботи Приймає рішення, чи можна рухати зміни далі
Може бути частковим і точковим Зазвичай обовʼязковий і фіксований
Використовується до, під час і після зміни Використовується на межі merge / release
Допомагає думати й швидко ловити поломки Допомагає не пропустити ризиковану зміну
Налаштований під щоденну швидкість роботи Налаштований під надійність командного процесу

Якщо зовсім коротко, harness відповідає на питання «що мені перевірити просто зараз, поки я змінюю код?», а quality gate — на питання «чи має команда право пропустити цю зміну далі?». Сьогодні нас цікавить саме перше.

У harness є три дуже практичні точки застосування — до, під час і після зміни:

Момент роботи Що робить harness
До першого редагування знаходить наявні команди, тести, місця використання, точки ризику
Після маленького кроку запускає formatter, lint, точковий тест, іноді type-check
Перед PR запускає повний набір потрібних перевірок: build, повний тестовий прогін, coverage та інші дорожчі сенсори

Якщо плутати harness із quality gate, майже неминуче поїдете в одну з двох крайнощів: або важкі перевірки занадто рано й часто, або все до кінця, а потім героїчно зʼясовуєте, на якому з чотирьох refactor-кроків розвалилася збірка.

І тут спливає питання: що саме ці перевірки мають утримати незмінним зовні, поки ви змінюєте форму коду всередині.

4. Claude знаходить harness, а не вигадує

Найдорожча помилка в AI-assisted розробці виглядає невинно. Ви просите: «Подивися, як краще перевіряти цей код». А Claude філософствує: додамо лінтер, змінимо плагіни, заведемо інший formatter, а краще перебудуємо весь toolchain, бо «сучасніше». Звучить жваво, для задачі — шкідливо.

У проєкті має бути одне джерело правди про локальні перевірки. У нашому курсовому контексті цю роль зазвичай виконує CLAUDE.md: команда фіксує, які команди запускати, що вважати швидким набором, а що — повним. Для Commerce OS це може виглядати так:

# Локальний verification harness

- format: `./gradlew spotlessApply`
- lint: `./gradlew check -x test`
- build: `./gradlew assemble`
- tests-fast: `./gradlew test --tests '*OrdersUnitTest'`
- tests-full: `./gradlew test`
- coverage: `./gradlew jacocoTestReport`
- frontend-lint: `npm run lint --workspace web`
- frontend-typecheck: `npm run typecheck --workspace web`

Використовуйте наявні скрипти та шаблони. Не впроваджуйте новий toolchain.

Цей запис важливий не лише для людини, а й для Claude — він перетворює розпливчасте «перевір якось» на інженерний контракт: ось що тут вважається harness. Потрібних рядків у CLAUDE.md немає? Claude іде в build.gradle, package.json, README, скрипти й повертає знайдену карту з позначками невизначеності. Але не фантазує.

Хороший запит до Claude в такому разі виглядає так:

Перед зміною в OrderService:
1) знайди локальний verification harness у CLAUDE.md, Gradle і package.json;
2) розділи перевірки на швидкі та повні;
3) запропонуй мінімальний набір сенсорів для цього кроку;
4) не додавай новий toolchain;
5) після виконання покажи, що реально запускалося.

Це дуже корисна звичка: спочатку просити Claude виявити наявні правила проєкту, а вже потім застосовувати їх. Особливо у великих репозиторіях, де «очевидна» команда ./gradlew test занадто важка для кожного кроку, а точковий прогін уже вбудований у проєкт і чекає, коли ви перестанете геройствувати й прочитаєте CLAUDE.md.

5. Швидкі та дорогі сенсори: що коли запускати

Якщо запускати повний набір після кожного редагування, робочий процес швидко починає нагадувати покарання за минулі гріхи. Якщо до самого кінця не запускати майже нічого, ви отримуєте класичний жанр «у мене ж локально все працювало». Тому harness корисно ділити ще й за вартістю запуску.

Дешеві: formatter, легкий lint, фронтендний type-check на невеликій ділянці, швидкий unit test — для кожного кроку. Середні: точковий integration test, локальна збірка модуля, вибірковий прогін по пакету. Дорогі: повний test, звіт покриття, широкий статичний аналіз — їм місце ближче до кінця етапу, а не всередині кожного редагування.

Ось простий робочий набір для Commerce OS:

./gradlew spotlessApply                    # швидкий недорогий сенсор
./gradlew test --tests '*OrdersUnitTest'  # перевірка після маленького кроку
npm run typecheck --workspace web         # швидкий контроль фронтенду
./gradlew assemble                        # локальна перевірка збірки
./gradlew test                            # повний прогін перед PR

Зверніть увагу на логіку, а не на конкретні назви — команди у вашому проєкті інші. Але принцип один: маленька зміна — маленька перевірка, а не запуск усього, що відоме людству. І навпаки: перед «готово» не можна обмежитися автоформатуванням. Інакше вийде дуже стильна, дуже акуратно вирівняна поломка.

Тут добре працює правило: автоматизуйте дешеве, не автоматизуйте дороге без причини. Хук у Workflow Kit, що запускає форматування після редагування, — чудовий дешевий сенсор. А повний backend-набір після кожного Edit хороший лише для тих, хто дуже любить чекати. Решта воліє жити.

Ще один важливий момент: якщо ви підключаєте tester-agent або reviewer-agent, їх теж потрібно тримати в межах harness. Tester-agent не вигадує екзотичні команди. Reviewer-agent не пише «перевірку не проводили», якщо проєкт вимагає після локального кроку швидкий unit suite. Інакше агент перетворюється на того колегу, який любить поради, але не любить читати правила проєкту.

6. Code intelligence: blast radius заздалегідь

Є дуже підступний тип рефакторингових помилок: код змінюється красиво, форматування ідеальне, unit tests зелені — а проблема сидить там, де метод або тип використовується опосередковано. Ось тут на сцену й виходить code intelligence. Звучить так, ніби IDE вирішила стати мудрецем, але все цілком земне: пошук посилань, ієрархія викликів, навігація за символами, діагностика мовного сервера. Для refactor це майже завжди корисніше за есе на тему «цей код виглядає трохи заплутано».

Припустімо, у Commerce OS ви хочете трохи впорядкувати OrderService.finalizeOrder. Наївний шлях — відкрити файл і «акуратно» виносити логіку. Більш зрілий — спершу попросити Claude повернути карту впливу:

Перед зміною `finalizeOrder` знайди:
- де оголошено метод;
- хто його викликає;
- які тести покривають цей шлях;
- від яких публічних контрактів він залежить;
- які файли точно не варто чіпати на цьому кроці.
Поверни лише підтверджені посилання на файли та символи.

Це дуже цінний сенсор до зміни. Він не каже «код правильний» і не замінює тести, але різко зменшує шанс, що ви візьметеся робити extract helper у методі, повʼязаному ще й із зовнішнім API, про який забули. Немає повноцінного code intelligence — не біда: пошук у проєкті, grep, навігація IDE, find usages. Просто з Claude цей етап швидший.

Важливо й те, що code intelligence зменшує blast radius — область можливого ураження, а це безпосередньо впливає на розмір diff. Коли ви заздалегідь знаєте, що зачеплено один сервіс і один набір unit-тестів, ви не чіпаєте «заодно» сусідні модулі. А маленький diff, як ви вже знаєте, — найкращий друг розробника, ревʼюера і вашого майбутнього «я», яке через два дні намагатиметься зрозуміти, що тут сталося.

7. Сценарій у Commerce OS перед refactor-кроком

Давайте зберемо все разом на короткому, але реалістичному сценарії. Ви хочете трохи почистити логіку в OrderService.finalizeOrder: винести блок валідації в private helper, не змінюючи зовнішній контракт методу. Не фіча, не bugfix, не міграція — той самий тип безпечної структурної зміни, заради якого потрібен сьогоднішній інструментарій.

Спочатку Claude читає CLAUDE.md і карту harness, через code intelligence дивиться usages і повʼязані тести, ви разом обираєте маленький крок — а не «рефакторинг усього OrderService, поки душа просить». Далі — код: точкова перевірка, читання diff, ближче до завершення ширший прогін:

flowchart TD
    A[Task spec на маленький рефакторинг] --> B[Прочитати CLAUDE.md і знайти harness]
    B --> C[Code intelligence: usages і call chain]
    C --> D[Вибрати мінімальний набір checks]
    D --> E[Зробити одну маленьку зміну]
    E --> F[Запустити точковий сенсор]
    F --> G[Прочитати diff]
    G --> H[Перед PR запустити ширший прогін]

Саме так harness перестає бути абстрактною «ідеєю про перевірки» і стає нормальним робочим процесом. Без спецефектів — а отже, добрим. В інженерії частіше перемагає не той, хто більше сподівається на інтуїцію, а той, хто заздалегідь розставив сенсори й тепер спокійно працює, а не вгадує, де в проєкті вже тліє наступна проблема.

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