Зачем это нужно
AI-агент может написать код очень быстро. Это главный плюс и одновременно главный риск.
Человек обычно пишет медленнее. Он дольше держит задачу в голове, чаще видит локальные детали, иногда интуитивно замечает несостыковки. AI может за минуты создать большой diff: новые файлы, tests, migrations, routes, UI states, docs. Но скорость генерации не равна скорости понимания и не равна качеству результата.
Поэтому в agentic development меняется баланс:
Generation speed ↑
↓
Required verification ↑
Чем быстрее агент производит изменения, тем сильнее должен быть слой проверки. Иначе команда начинает принимать код по уверенности текста, а не по evidence.
Главная цель урока - понять, почему tests, typecheck, lint, build и acceptance checks становятся центральным механизмом доверия к AI-generated changes.
Основная идея
В традиционной разработке тесты нужны, потому что люди ошибаются.
В AI-assisted development тесты нужны еще сильнее, потому что:
- код генерируется быстрее;
- diff может быть больше;
- агент может звучать уверенно даже при ошибке;
- модель может неправильно понять spec;
- модель может пропустить edge case;
- модель может не заметить нарушение существующего контракта;
- модель может "проверить логически", но не выполнить код;
- один человек может запускать несколько agent tasks подряд.
Тесты становятся не просто quality practice, а control system.
Формула:
Trust the evidence, not the narration.
Агент может объяснить, что он сделал. Но принимать результат стоит по проверяемым сигналам:
- tests passed;
- typecheck passed;
- lint passed;
- build passed;
- acceptance criteria mapped;
- diff reviewed;
- failures reported honestly.
Ключевые термины
| Термин | Значение |
|---|---|
| Verification | Проверка, что изменение работает и не нарушает важные ограничения. |
| Evidence | Проверяемое подтверждение результата: команда, exit code, test result, diff, AC status. |
| Self-report | Утверждение агента о том, что все работает. Само по себе не является доказательством. |
| Baseline | Состояние проверок до изменения или перед началом task. |
| Regression | Поломка ранее работавшего поведения. |
| Acceptance Check | Проверка, что acceptance criterion выполнен. |
| Automated Test | Проверка, выполняемая машинно и повторяемо. |
| Manual Check | Проверка человеком или агентом без автоматизированного утверждения. |
| Typecheck | Проверка типов без запуска runtime behavior. |
| Lint | Проверка стиля, потенциальных ошибок и соглашений. |
| Build | Проверка, что проект собирается. |
| Test Signal | Информация, которую дает test: pass, fail, error, skipped, flaky, not run. |
Почему скорость меняет требования
Когда скорость генерации растет, risk profile меняется.
Если раньше разработчик мог вручную проверить маленький patch, то с агентом возникает другой режим:
Много маленьких diff быстро
или
один большой diff слишком быстро
В обоих случаях manual checking начинает отставать. Поэтому автоматическая verification становится ограничителем скорости:
AI generation speed can increase only as far as verification can keep up.
Self-report агента не доказательство
Агент может написать:
Готово, я проверил, все работает.
Но это может означать разные вещи:
- агент реально запустил tests;
- агент посмотрел на код и решил, что должно работать;
- агент хотел запустить tests, но не смог;
- агент перепутал command output;
- агент не заметил failing test;
- агент проверил не тот scope;
- агент говорит уверенно, потому что так выглядит хороший финальный ответ.
Поэтому final answer должен содержать evidence.
Плохо:
Готово. Тесты должны проходить.
Лучше:
Verification:
- `npm test -- tests/api/projects.test.ts`: passed, exit code 0
- `npm run typecheck`: passed, exit code 0
Acceptance criteria:
- valid create: satisfied by API test
- invalid name: satisfied by API test
Если проверки не запускались:
Verification:
- `npm test -- tests/api/projects.test.ts`: not run
Reason: dependencies are not installed.
Residual risk:
- Runtime behavior has not been verified locally.
Честный "not run" лучше, чем красивое "should work".
Evidence-based completion
Task завершена не тогда, когда агент закончил писать код. Task завершена, когда есть evidence.
Минимальная evidence для coding task:
| Evidence | Пример |
|---|---|
| Changed files | src/api/projects.ts, tests/api/projects.test.ts |
| Targeted test | npm test -- tests/api/projects.test.ts |
| Exit code/result | passed, exit code 0 |
| Type/lint/build | npm run typecheck, npm run lint, npm run build |
| AC status | AC1 satisfied, AC2 satisfied |
| Diff review | no unrelated changes |
| Limitations | full suite not run, DB unavailable |
Evidence должна быть конкретной. Не нужно длинное эссе. Нужно, чтобы человек мог понять:
Что изменилось?
Чем проверено?
Что осталось непроверенным?
Baseline перед изменениями
Baseline - это знание о состоянии проекта до изменения.
Зачем он нужен:
- отличить новую поломку от уже существующей;
- понять, какие проверки доступны;
- увидеть flaky или failing tests заранее;
- выбрать targeted verification;
- не обвинять agent diff в старых проблемах;
- не принимать new failure как "так и было" без доказательств.
Baseline может быть легким:
Перед task:
- `npm test -- tests/api/projects.test.ts`: passed
- `npm run typecheck`: passed
Или ограниченным:
Baseline not run:
- Task is small docs-only change.
- Existing CI status is unknown.
Не всегда нужно запускать весь suite до каждого изменения. Но для рискованных задач полезно знать хотя бы ближайший baseline.
Когда baseline особенно важен
Baseline стоит сделать, если:
- task затрагивает shared module;
- test suite уже подозрительно нестабилен;
- нужно исправить bug;
- меняется architecture boundary;
- меняется database schema;
- меняется public API;
- несколько agents работают параллельно;
- рабочее дерево уже dirty;
- expected failure нужно воспроизвести до fix.
Для bug fix baseline часто выглядит как reproduction:
Before fix:
- `pytest tests/auth/test_reset_token.py::test_reuse_rejected`: failed
After fix:
- same test: passed
Это сильная evidence: bug действительно был воспроизведен и исправлен.
Manual check vs automated evidence
Manual checking полезен, но плохо масштабируется.
| Критерий | Manual check | Automated evidence |
|---|---|---|
| Повторяемость | Низкая | Высокая |
| Скорость на regression | Низкая | Высокая |
| Документируемость | Часто слабая | Команда и result |
| Покрытие edge cases | Зависит от человека | Можно фиксировать cases |
| Усталость | Высокий риск | Ниже |
| Подходит для UI feel | Да | Частично |
| Подходит для API contract | Плохо | Хорошо |
| Подходит для bug fix | Только как дополнение | Лучше через regression test |
Manual check не запрещен. Он нужен для:
- visual quality;
- UX feel;
- exploratory testing;
- accessibility review;
- сложных edge cases;
- поведения, которое трудно автоматизировать быстро.
Но manual check должен быть явно назван:
Manual check:
- Opened settings page.
- Created a project through UI.
- Verified success toast and navigation.
Automated checks:
- not available for this UI flow.
Residual risk:
- no regression coverage for browser behavior.
"Агент проверил глазами" не равно test
Агент может сделать static reasoning:
Я просмотрел код, логика корректна.
Это полезно, но это не automated verification.
Разделяйте:
| Тип проверки | Что доказывает |
|---|---|
| Static inspection | Код выглядит согласованным с pattern. |
| Typecheck | Типы согласованы. |
| Lint | Нет части статических нарушений. |
| Unit test | Локальная logic работает на cases. |
| Integration test | Компоненты вместе дают expected behavior. |
| E2E test | User/system flow работает целиком. |
| Contract test | Boundary соответствует contract. |
| Manual check | Человек или агент проверил сценарий руками. |
Static inspection - это слой. Но не единственный.
Роль tests в agent workflow
Tests нужны агенту не только в конце. Они управляют всем loop.
Тест дает агенту обратную связь. Без теста агенту приходится полагаться на собственную уверенность.
AI особенно хорошо работает, когда у него есть fast feedback:
- failing test;
- compiler error;
- type error;
- lint output;
- snapshot diff;
- contract mismatch;
- migration failure.
Feedback превращает модель из генератора текста в участника engineering loop.
Что проверять при agent-generated change
Минимальный набор зависит от задачи.
Для backend endpoint:
- API/integration test;
- service/unit test, если есть pattern;
- typecheck;
- contract check, если есть OpenAPI;
- lint, если быстрый.
Для frontend form:
- component test;
- validation cases;
- accessibility smoke check;
- visual/screenshot check, если UI важен;
- typecheck/build.
Для database migration:
- migration apply check;
- schema diff;
- model tests;
- backward compatibility check;
- rollback/dry-run, если проект требует.
Для docs-only change:
- markdown lint, если есть;
- link check, если есть;
- review diff.
Для bug fix:
- reproduction;
- failing regression test before fix;
- targeted regression after fix;
- broader tests if shared behavior changed.
Verification ladder
Если невозможно выполнить идеальный test, используйте ladder.
Best:
Automated test proving AC.
Good:
Targeted test + typecheck/lint.
Acceptable with caveat:
Static inspection + manual check + explicit residual risk.
Bad:
"The agent thinks it works."
Смысл ladder не в том, чтобы оправдывать отсутствие тестов. Смысл в том, чтобы честно назвать уровень confidence.
Verification budget
Проверки стоят времени. Нельзя всегда запускать все.
Нужно выбирать:
- closest test first;
- cheap static checks early;
- broader suite when shared code changed;
- expensive E2E only when risk оправдан;
- manual check when automation unavailable.
Пример:
Change: validation schema for one endpoint.
Run:
1. endpoint API test
2. validation unit test if exists
3. typecheck
Skip:
- full E2E suite unless endpoint affects core flow
Хороший агент объясняет, почему выбран такой набор verification.
Tests как защита от AI failure modes
| AI failure mode | Как помогает testing |
|---|---|
| Hallucinated API | Typecheck/build ловят nonexistent imports/functions. |
| Misread requirement | AC-to-test traceability показывает mismatch. |
| Overbroad refactor | Diff review + regression tests ловят unintended changes. |
| Missing edge case | Explicit test cases фиксируют boundary behavior. |
| Wrong integration assumption | Integration test проверяет реальные boundaries. |
| Silent failure | Exit codes и logs дают evidence. |
| Confident false claim | Test result важнее self-report. |
| Stale context | Running tests проверяет текущий код, а не память модели. |
Тесты не делают модель безошибочной. Они делают ошибки видимыми.
Acceptance criteria и tests
Каждый важный acceptance criterion должен иметь:
automated test
или
explicit manual check
или
осознанный residual risk
Пример:
| Acceptance Criterion | Verification |
|---|---|
| Valid project name creates project | API integration test |
| Empty name rejected | API validation test |
| Owner is current user | Service/API test assertion |
| Response returns project id | API integration test |
| UI button disabled while loading | Component test or manual UI check |
Это мост к уроку 9.3 Specification -> Tests.
Baseline checklist
Перед изменением:
[ ] Прочитан spec
[ ] AC выписаны
[ ] Найдены existing tests
[ ] Понятно, какую проверку запускать
[ ] При bug fix воспроизведен failure
[ ] Известны текущие failing tests, если они есть
[ ] Понятны commands для targeted verification
Если baseline не сделан, это не всегда блокер. Но агент должен понимать, что confidence ниже.
Completion checklist
Перед финальным ответом:
[ ] Tests added/updated where practical
[ ] Targeted tests run
[ ] Typecheck/lint/build run when relevant
[ ] Failed checks reported
[ ] Not-run checks explained
[ ] Diff reviewed
[ ] AC status reported
[ ] Residual risk named
Эта checklist должна стать привычкой. Без нее agent-generated changes выглядят быстрее, чем они реально безопасны.
Пример: без verification
Task:
Добавь endpoint POST /api/projects.
Плохой финал:
Готово. Я добавил endpoint, validation и service. Все должно работать.
Что не так:
- нет changed files;
- нет tests;
- нет command output;
- нет AC mapping;
- нет diff review;
- "должно работать" не evidence.
Пример: с evidence
Хороший финал:
Summary:
- Added POST /api/projects endpoint with request validation and service integration.
Changed files:
- src/api/projects.ts
- src/services/projects.ts
- tests/api/projects.test.ts
Verification:
- npm test -- tests/api/projects.test.ts: passed, exit code 0
- npm run typecheck: passed, exit code 0
Acceptance criteria:
- Valid name creates project: satisfied by API test.
- Empty name returns validation error: satisfied by API test.
- Project belongs to current user: satisfied by service assertion.
- Response returns 201 with project id: satisfied by API test.
Residual risk:
- Full E2E suite was not run.
Такой ответ можно проверить, обсудить и принять в review.
Что делать с failing checks
Falling check - это сигнал, не стыд.
Агент должен:
- Сохранить точный command и failure summary.
- Понять, failure связан с текущим diff или baseline.
- Исправить, если failure в scope.
- Не исправлять unrelated failure без разрешения.
- Не ослаблять test ради зеленого результата.
- Сообщить residual risk, если failure остается.
Пример:
Verification:
- npm test -- tests/api/projects.test.ts: failed, exit code 1
Failure: expected 201, got 401.
Analysis:
- Failure is in scope: endpoint uses auth middleware but test lacks authenticated user.
Next step:
- Update test setup to use existing authenticated helper.
Или:
Verification:
- npm test: failed in unrelated `billing.test.ts`.
Analysis:
- Billing test was failing before this task according to baseline.
- Current endpoint test passed.
Residual risk:
- Full suite remains red; not caused by this diff based on baseline.
Testing не заменяет review
Passing tests не означают, что diff идеален.
Tests могут не ловить:
- unnecessary complexity;
- bad naming;
- architecture drift;
- missing observability;
- security design flaw;
- wrong abstraction;
- poor UX;
- incomplete docs;
- performance risk.
Поэтому loop остается:
tests + diff review + AC check
А не только:
tests pass
Testing не заменяет specification
Тесты отвечают:
Проверяется ли конкретное поведение?
Spec отвечает:
Какое поведение должно быть?
Если spec плохая, агент может написать tests на неправильное поведение.
Поэтому порядок важен:
Spec -> Acceptance Criteria -> Tests -> Implementation -> Verification
Не наоборот.
Как встроить это в prompts
Плохой prompt:
Сделай фичу.
Лучше:
Сделай фичу через evidence-based loop.
Перед изменениями:
- прочитай spec;
- выпиши acceptance criteria;
- найди existing tests;
- предложи verification plan.
Во время работы:
- добавь или обнови targeted tests where practical;
- реализуй минимальный diff.
Перед финалом:
- запусти targeted tests;
- запусти typecheck/lint/build if relevant;
- проверь diff;
- сопоставь AC с tests/results;
- сообщи commands, exit codes, failures и residual risk.
Такой prompt задает стандарт доверия.
Практика: verification checklist для task
Task:
Bug: reset password token can be reused after successful reset.
Заполните:
Acceptance criteria:
1.
2.
3.
Baseline/reproduction:
- command:
- expected before fix:
Test to add/update:
- file:
- scenario:
Verification after fix:
- targeted test:
- broader test:
- typecheck/lint/build:
Manual checks, if any:
Residual risk:
Возможное заполнение:
Acceptance criteria:
1. Consumed reset token cannot be reused.
2. Expired token is rejected.
3. Successful reset invalidates token.
Baseline/reproduction:
- command: pytest tests/auth/test_password_reset.py::test_reset_token_reuse
- expected before fix: fails because reused token is accepted
Test to add/update:
- file: tests/auth/test_password_reset.py
- scenario: use token once, then attempt second reset with same token
Verification after fix:
- targeted test: pytest tests/auth/test_password_reset.py
- broader test: pytest tests/auth
- typecheck/lint/build: project-specific commands if available
Residual risk:
- email delivery flow not covered by this regression test
Практика: manual vs automated evidence
Сравните два финальных отчета.
Report A
Готово. Я вручную просмотрел код, проблема исправлена.
Report B
Changed files:
- app/auth/reset.py
- tests/auth/test_password_reset.py
Verification:
- pytest tests/auth/test_password_reset.py::test_reset_token_reuse: passed, exit code 0
- pytest tests/auth: passed, exit code 0
Acceptance criteria:
- token cannot be reused: satisfied by regression test
Residual risk:
- full application suite not run
Вопросы:
- Какой отчет можно использовать в review?
- Какой отчет помогает future regression protection?
- Какой отчет честнее говорит о residual risk?
Ответ очевиден, но в реальных проектах команды часто принимают Report A, потому что агент звучит уверенно. Вот почему нужна дисциплина.
Типичные ошибки
| Ошибка | Симптом | Исправление |
|---|---|---|
| Верить self-report | "Agent said it works" | Требовать commands/results. |
| Не делать baseline | Непонятно, новая ли поломка | Запускать targeted baseline where useful. |
| Нет test для bug fix | Bug может вернуться | Добавить regression test. |
| Только manual check | Сложно повторить | Автоматизировать key AC. |
| Игнорировать failed tests | "Потом починим" | Report and classify failure. |
| Ослаблять tests | Suite зеленеет, behavior ломается | Tests должны следовать spec. |
| Запускать только full suite | Медленно и шумно | Start targeted, broaden by risk. |
| Не сообщать not-run checks | Ложное confidence | Explicit limitation. |
| Не связывать tests с AC | Проверки есть, но feature неполная | AC-to-test mapping. |
| Tests без diff review | Passing tests, но плохой scope | Review diff separately. |
Критерии готовности
После урока студент должен уметь:
- объяснить, почему AI увеличивает потребность в verification;
- отличить self-report от evidence;
- назвать минимальные элементы evidence-based completion;
- объяснить baseline и когда он нужен;
- сравнить manual check и automated evidence;
- выбрать проверки для backend/frontend/migration/docs/bug fix tasks;
- составить verification checklist;
- честно описать not-run checks и residual risk.
Краткое резюме
AI ускоряет генерацию кода. Поэтому tests и другие проверки становятся важнее, а не менее важными.
Главная формула:
Generation speed ↑
↓
Required verification ↑
И главный принцип:
Trust evidence, not narration.
Evidence-based completion требует changed files, commands, results, acceptance criteria status, diff review и residual risk. Агент может быть полезным генератором изменений только тогда, когда система проверки успевает за скоростью генерации.
Вопросы для самопроверки
- Почему self-report агента не считается доказательством?
- В каких задачах baseline особенно важен?
- Чем manual check хуже automated evidence, и когда он все равно нужен?
- Что должен содержать final verification report?
- Почему passing tests не заменяют diff review и acceptance criteria check?
Что дальше
В следующем уроке мы разберем test pyramid: unit, integration, E2E, contract, snapshot, visual, migration, security и performance checks. Нам нужно научиться выбирать не просто "писать тесты", а какой уровень теста подходит к конкретному acceptance criterion и риску изменения.