JavaRush /Курси /Claude code /Automation hooks: сходи розгортання

Automation hooks: сходи розгортання

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

1. Ознаки справді корисного hook

Після знайомства з event, matcher і handler дуже хочеться одразу натиснути внутрішню червону кнопку «автоматизувати все». Це нормальне бажання: якщо інструмент уміє реагувати на події, значить, на нього можна повісити що завгодно. Але тут важливо вчасно пригальмувати — одні задачі hook вирішує добре, інші лише додають шуму.

Як зрозуміти, що перед вами хороший кандидат на автоматизацію? У корисного hook майже завжди чотири ознаки: він спрацьовує в повторюваній ситуації, дає передбачуваний результат, не потребує тонкого людського смаку й легко відкочується. Форматування після редагування — хороший кандидат, підказка «ви зачепили чутливий шлях, запустіть перевірку» — теж. А «оцінити, наскільки архітектурно красивий мій код» — уже не hook-задача: ні детермінізму, ні дешевого відкату, ні гарантії, що автоматизація не почне філософствувати замість допомоги.

Для Workflow Kit команди Commerce OS найкорисніше починати з дуже земних сценаріїв: formatter приводить код до одного стилю; linter шукає підозрілі конструкції до ручної перевірки; вузький захисний hook не дає полізти в чутливий шлях; report-hook нагадує, які перевірки важливі після конкретного модуля. Спочатку маленький сигнал, потім усвідомлена дія — а не «я повісив automation, далі сама розбереться».

Точні імена подій, полів matcher і прапорців блокування можуть відрізнятися у вашій версії Claude Code. Орієнтуйтеся на /hooks і актуальну документацію. Важлива логіка, а не конкретний JSON-синтаксис, висічений у камені.

2. Безпечні сценарії для перших hooks

Коли ви тільки починаєте працювати з hooks, перший hook — не час геройствувати. Це як ніж на кухні: спочатку ви ріжете хліб, а не намагаєтеся одразу жонглювати трьома клинками. Беріть вузькі сценарії, де побічний ефект легко помітити й прибрати.

Нижче зручна карта перших сценаріїв:

Сценарій Що робить hook Чому це хороший старт
Форматування після редагування Проганяє форматувач по одному зміненому файлу Зміна локальна, зрозуміла, зазвичай легко відкочується
Мʼякий lint/report Показує попередження або запускає коротку перевірку Дає швидкий сигнал, але не блокує роботу
Нагадування про чутливий модуль Повідомляє, що після зміни варто запустити додаткові перевірки Не лізе в код, але зменшує шанс забути важливий крок
Вузький захист шляху Блокує запис у конкретну небезпечну зону Корисно, але лише після акуратного розгортання
Контекст на початку сесії Друкує коротку памʼятку або команди проєкту Не втручається в код, зате зменшує побутові помилки

Форматування — майже ідеальна перша автоматизація. Якщо в команди вже вибрано formatter, hook не «думає», а просто застосовує заздалегідь узгоджене правило. Не заміна review, а спосіб не витрачати ревʼю на суперечки про пробіли й лапки. Новачку до того ж легше читати однаково оформлений код.

Мʼякий lint або report-hook добрий саме тому, що не вдає із себе начальника. Він не каже «стій, не пущу», а каже «я помітив корисний сигнал». Особливо в Commerce OS, де фронтенд на Next.js і бекенд на Java живуть поруч: у різних частин проєкту перевірки різні — hook тут краще спочатку використовувати як ранній радар, а не як мініохорону на прохідній.

З нагадуваннями для чутливих модулів узагалі виходить дуже людський сценарій. Уявіть, що ви редагували шлях про повернення чи платежі; hook не зобовʼязаний ганяти важкі тести, але може нагадати: «Ви зачепили модуль refunds, не забудьте targeted checks». Новачку такий сигнал особливо корисний: інженерна мʼязова памʼять формується не афоризмами, а повторенням маленьких ритуалів.

3. Non-blocking і blocking: різний характер

Ззовні два hook можуть виглядати майже як близнюки: той самий event, схожий matcher, майже той самий handler. Але за реальним ефектом це дві різні тварини. Non-blocking — ввічливий колега, зазирнув через плече: «Тут, здається, варто перевірити ще дещо». Blocking — турнікет, який не пустить далі, доки умову не виконано. Помилка в першому дратує, у другому — зупиняє роботу.

Різницю зручно побачити на контрасті:

Режим Що відбувається під час спрацювання Де використовувати першим
Non-blocking Hook пише лог, форматуває, показує сигнал, але не скасовує дію Майже завжди це найкращий старт
Blocking Hook може заборонити дію або вимагати додаткового кроку Лише в дуже вузьких і перевірених сценаріях

Ось мінімальний non-blocking hook для стадії спостереження:

{
  "event": "afterFileEdit",
  "matcher": { "paths": ["frontend/src/**/*.{ts,tsx}"] },
  "handler": {
    "command": "scripts/log-format-candidate.sh {{file}}",
    "blocking": false
  }
}

А ось вузький blocking-приклад для чутливого шляху:

{
  "event": "beforeToolUse",
  "matcher": { "tool": "Write", "paths": ["**/payments/**"] },
  "handler": {
    "command": "scripts/block-payment-write.sh {{file}}"
  }
}

Сенс тут не в синтаксисі, а в характері втручання. Помилився non-blocking — зайвий шум у логах, переживемо. Помилився blocking — почнете сперечатися зі своїм же automation-шаром, як із впертим шлагбаумом на парковці: і машина ваша, і двір ваш, а шлагбаум усе одно впевнений, що ви тут сторонні.

Саме тому на ранньому етапі майже все має бути non-blocking. Blocking вмикайте лише там, де matcher дуже вузький, сценарій стабільний, а ціна хибного спрацювання команді зрозуміла заздалегідь. Інакше ви будуєте не safety rail, а випадковий генератор фрустрації.

4. Сходи розгортання: hook по кроках

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

Цю ідею зручно тримати у вигляді сходів розгортання:

flowchart TD
    A["Log-only"] --> B["Non-blocking"]
    B --> C["Formatting"]
    C --> D["Test / report"]
    D --> E["Narrow blocking"]
    E --> F["Policy-managed"]

На першій сходинці log-only hook взагалі нічого не змінює, лише пише: «Я спрацював на такому-то файлі». Нудно, але безцінно: одразу видно, чи не надто широкий matcher.

На другій сходинці non-blocking hook уже може робити мʼяку дію: підказка, невеликий report, дешева перевірка. Це все ще спостереження, але вже прикладне: видно, чи допомагає automation, чи просто прикрашає термінал новими рядками.

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

Четверта сходинка — test/report, підключення до ранніх перевірок. Тут важливо не переплутати корисну валідацію з персональним CI, який запускається на кожен чих і з’їдає темп. Хороший test/report-hook або гоняє коротку локальну перевірку, або нагадує, яку не забути.

Пʼята сходинка — narrow blocking. Ось тут automation уже може сказати «стоп», але дуже локально: запис у чутливий шлях без підтвердження, небезпечна команда в захищеній зоні. Розповзеться широко — команда почне ненавидіти не ризик, а сам інструмент.

Шоста сходинка — policy-managed. Це вже не особистий експеримент, а зрілий артефакт Workflow Kit: зрозумілий, обкатаний, задокументований, легко вимикається, не лякає команду. До цієї сходинки доростають, а не перестрибують через три прольоти одразу.

5. format-on-edit для Workflow Kit на практиці

Тепер зберемо все в один наскрізний приклад і зафіксуємо робочий baseline для format-on-edit: вузький matcher, non-blocking handler і guard для крайніх випадків. Мета — виростити живучий артефакт .claude/hooks/format-on-edit.json, який команда Commerce OS розгорне без щоденних молитов про стабільність.

Спочатку ми взагалі не форматуватимемо код — перевіряємо лише, що matcher ловить потрібні файли фронтенду й не лізе в бекенд, generated-файли та випадкові артефакти. Для цього hook іде в log-only:

{
  "event": "afterFileEdit",
  "matcher": { "paths": ["frontend/src/**/*.{ts,tsx}"] },
  "handler": {
    "command": "scripts/log-format-candidate.sh {{file}}",
    "blocking": false
  }
}

Сам скрипт може бути зовсім маленьким:

#!/usr/bin/env bash
FILE="$1"

echo "[hook] кандидат на форматування: $FILE" >&2
# [hook] кандидат на форматування: frontend/src/app/page.tsx
exit 0

Тут немає жодної магії. Кілька робочих сесій я просто спостерігаю, чи справді hook спрацьовує там, де потрібно. Зловив зайві файли — ось ідеальний момент поправити matcher, поки він нікому нічого не ламає.

Коли стало ясно, що matcher поводиться чемно, можна дати hook право на реальне форматування:

{
  "event": "afterFileEdit",
  "matcher": { "paths": ["frontend/src/**/*.{ts,tsx}"] },
  "handler": {
    "command": "pnpm prettier --write {{file}}",
    "blocking": false
  }
}

Якщо проєкту потрібен додатковий захист від зайвих файлів, краще загорнути команду у свій скрипт і зробити ранній вихід для винятків:

#!/usr/bin/env bash
FILE="$1"

case "$FILE" in
  *.generated.ts|*.snap) exit 0 ;;
esac

pnpm prettier --write "$FILE"

Саме така звʼязка — вузький matcher плюс guard у handler — зазвичай і стає живучим baseline для проєкту.

На цьому місці багато розробників надихаються й хочуть одразу додати ще й ESLint, і тести, і перевірку імпортів, і, мабуть, нагадування пити воду. Краще зупинитися. Один hook на пʼять справ перетворюється на комбайн, який складніше зрозуміти, ніж вимкнути. Хороший Workflow Kit живе навпаки: один артефакт — одна зрозуміла відповідальність.

Зрілий format-on-edit виглядає нудно — і це комплімент. Нудна автоматизація — найкраща автоматизація.

6. Hooks для перевірки й тестів: швидкий сигнал, а не CI

Після форматування наступна спокуса — перевірки. Здається: раз hook уміє запускати команду, значить після кожного редагування треба ганяти тести, лінтер, збірку і весь проєкт через статичний аналіз. Практично одне редагування перетворюється на маленьку чергу на посадку в аеропорту.

Тому для validation і testing hooks діє просте правило: ранній сигнал має бути дешевшим, ніж помилка, яку він допомагає помітити. Перевірка важка, рідкісна або шумна — їй не місце в hook першого рівня, виносьте на пізніший етап workflow. Hook тут — швидкий сенсор, а не переносний дата-центр.

Для фронтенд-файлів можна почати з мʼякого lint по зміненому файлу:

{
  "event": "afterFileEdit",
  "matcher": { "paths": ["frontend/src/**/*.{ts,tsx}"] },
  "handler": {
    "command": "pnpm eslint {{file}}",
    "blocking": false
  }
}

А для чутливих зон бекенду частіше корисніше не запускати дорогі тести автоматично, а видати точну підказку:

{
  "event": "afterFileEdit",
  "matcher": { "paths": ["backend/src/main/java/**/refund/**"] },
  "handler": {
    "command": "scripts/report-refund-checks.sh {{file}}",
    "blocking": false
  }
}

Скрипт-підказка може бути дуже простим:

#!/usr/bin/env bash
FILE="$1"

echo "Змінений чутливий файл: $FILE" >&2
echo "Перевірте targeted tests для refund-сценаріїв" >&2
# Перевірте targeted tests для refund-сценаріїв

Тут hook не вдає з себе розумнішого за розробника. Він просто вчасно нагадує: «Ви зачепили модуль, де ціна забудькуватості вища за звичайну». Новачку такий мʼякий сигнал часто корисніший за автоматичний запуск важкої suite — він ще й привчає думати про ризик зміни.

Хороший testing hook — не той, що запускає все. Це hook, який приносить потрібний сигнал у потрібний момент і не зʼїдає темп. Боретеся після його ввімкнення не з багами, а з часом очікування — отже, автоматизація пішла далі своєї реальної цінності.

7. Ознаки того, що hook допомагає, а не заважає

У будь-якої автоматизації дуже простий іспит: після тижня використання команда має працювати спокійніше, а не напруженіше. Якщо hook спрацьовує не там, пише забагато, несподівано змінює файли, маскує початкову помилку своєю власною або вимагає окремого ритуалу вимкнення — він не дозрів. Не трагедія — сигнал повернутися на попередню сходинку сходів.

Корисно іноді ставити hook три запитання. Перше: чи можна пояснити його сенс одним абзацом без шаманства? Друге: чи можна вимкнути його одним зрозумілим дією? Третє: чи робить він проблему меншою, ніж сам додає шуму? Хоча б одна туманна відповідь — звузьте matcher, послабте втручання або поверніть hook у log-only.

Особливо важливо памʼятати про це в Workflow Kit, який із часом стає спільним артефактом команди. Особистий експеримент можна мовчки пережити. Спільний hook, який вередує, швидко підриває довіру до всього automation-шару. А довіра в інженерному workflow крихка: втрачається за один дивний вечір, повертається тижнями.

Тому зрілість hook майже завжди виглядає скромно. Він вирішує маленький повторюваний біль. Його легко зрозуміти й легко вимкнути. Він не намагається бути розумнішим за весь процес. Найкращий комплімент для automation: вона настільки доречна, що про неї майже перестають думати.

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