1. Refactor без 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`
Use existing scripts and patterns. Do not introduce a new toolchain.
Эта запись важна не только для человека, но и для Claude — она переводит расплывчатое «проверь как-нибудь» в инженерный контракт: вот что здесь считается harness. Нужных строк в CLAUDE.md нет? Claude идёт по build.gradle, package.json, README, скриптам и возвращает найденную карту с пометками неопределённости. Но не фантазирует.
Хороший запрос к Claude в таком случае выглядит так:
Перед изменением в OrderService:
1) найди локальный verification harness в CLAUDE.md, Gradle и package.json;
2) раздели проверки на быстрые и полные;
3) предложи минимальный набор sensors для этого шага;
4) не добавляй новый toolchain;
5) после выполнения покажи, что реально запускалось.
Это очень полезная привычка: сначала просить Claude обнаружить существующие правила проекта, а уже потом применять. Особенно в больших репозиториях, где «очевидная» команда ./gradlew test слишком тяжела для каждого шага, а точечный прогон уже зашит в проект и ждёт, когда вы перестанете геройствовать и прочитаете CLAUDE.md.
5. Быстрые и дорогие датчики: что когда запускать
Если запускать полный набор после каждого редактирования, рабочий процесс быстро начинает напоминать наказание за прошлые грехи. Если до самого конца не запускать почти ничего, вы получаете классический жанр «у меня же локально всё работало». Поэтому harness полезно делить ещё и по стоимости запуска.
Дешёвые: formatter, лёгкий lint, фронтендный type-check на небольшом участке, быстрый unit test — для каждого шага. Средние: точечный integration test, локальная сборка модуля, выборочный прогон по пакету. Дорогие: полный test, отчёт покрытия, широкий статический анализ — им место ближе к концу этапа, а не внутри каждого редактирования.
Вот простой рабочий набор для Commerce OS:
./gradlew spotlessApply # быстрый дешёвый sensor
./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 и связанные тесты, вы вместе выбираете маленький шаг — а не «refactor всего OrderService, пока душа просит». Дальше — код: точечная проверка, чтение diff, ближе к завершению более широкий прогон:
flowchart TD
A[Task spec на маленький refactor] --> B[Прочитать CLAUDE.md и найти harness]
B --> C[Code intelligence: usages и call chain]
C --> D[Выбрать минимальный набор checks]
D --> E[Сделать одно маленькое изменение]
E --> F[Запустить targeted sensor]
F --> G[Прочитать diff]
G --> H[Перед PR запустить более широкий прогон]
Именно так harness перестаёт быть абстрактной «идеей про проверки» и становится нормальным рабочим процессом. Без спецэффектов — а значит, хорошим. В инженерии чаще побеждает не тот, кто больше надеется на интуицию, а тот, кто заранее расставил датчики и теперь спокойно работает, а не гадает, где в проекте уже тлеет следующая проблема.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ