Зачем это нужно
В первых двух уроках мы разобрали две опорные идеи:
- LLM работает с ограниченным контекстом и может уверенно ошибаться.
- Coding agent действует итеративно через agent loop: observe, plan, act, observe result.
Теперь нужно понять, почему привычный запрос "сделай всю фичу целиком" ломает эту управляемость.
One-shot development выглядит привлекательно: один prompt, один agent run, много кода. Но в реальной разработке крупная фича почти всегда содержит десятки скрытых product, architecture, UX, data, security и testing решений. Если эти решения не вынесены в specification, design и tasks, агент начинает угадывать.
Главная цель урока - показать, что большая задача должна превращаться не в большой prompt, а в граф маленьких проверяемых задач.
Основная идея
One-shot development - это подход, при котором разработчик дает агенту крупную задачу одной командой и ожидает готовый результат без явной спецификации, декомпозиции и промежуточной проверки.
Пример:
Сделай password reset: backend, email, UI, тесты и документацию.
Выглядит понятно. Но внутри этой фразы спрятаны десятки решений:
- как долго действует token;
- можно ли использовать token повторно;
- что отвечать на неизвестный email;
- нужен ли rate limit;
- инвалидировать ли старые sessions;
- какой email template использовать;
- где хранить audit log;
- какие API endpoints добавить;
- какие тесты считать достаточными;
- какие файлы можно менять;
- какие architectural boundaries нельзя нарушать.
Если эти решения не сформулированы явно, агент не может "правильно" выполнить задачу. Он может только выбрать вероятный вариант.
Главный тезис:
Большая задача
!=
большой prompt
Большая задача должна становиться:
Specification
↓
Design
↓
Task graph
↓
Small verified changes
Ключевые термины
| Термин | Значение |
|---|---|
| One-shot development | Попытка реализовать крупную задачу одним запросом и одним длинным agent run. |
| Vibe coding | Разработка по общему намерению и ощущениям без строгой specification и verification. |
| Drift | Отклонение агента от исходной цели, architecture constraints или allowed scope. |
| Context pollution | Загрязнение контекста лишними деталями, устаревшей информацией и шумом. |
| Hidden decision | Решение, которое необходимо для реализации, но не было явно сформулировано человеком. |
| Local correctness | Код выглядит правильным в одном месте, но ломает поведение системы в целом. |
| Unrelated change | Изменение файла или поведения, которое не требуется текущей задачей. |
| Task graph | Граф маленьких задач и зависимостей между ними. |
Почему one-shot кажется рабочим
One-shot хорошо работает на маленьких изолированных задачах:
Напиши функцию форматирования даты.
или:
Объясни, почему этот тест падает.
Такие задачи имеют узкий контекст, небольшой результат и понятную проверку.
Проблема начинается, когда задача становится системной:
Добавь billing.
Сделай authentication.
Реализуй notifications.
Перепиши dashboard.
Оптимизируй backend.
В этих формулировках не хватает:
- observable behavior;
- acceptance criteria;
- non-goals;
- architecture constraints;
- allowed scope;
- verification commands;
- order of implementation.
Агенту приходится проектировать, реализовывать, тестировать и принимать product decisions одновременно. Это слишком много ответственности для одного неограниченного loop.
Что скрыто внутри большой задачи
Рассмотрим задачу:
Сделай систему уведомлений для проекта.
На первый взгляд это одна фича. На практике внутри нее несколько слоев решений.
| Слой | Скрытые вопросы |
|---|---|
| Product | Какие события должны создавать уведомления? Нужны ли read/unread states? |
| UX | Где показывать уведомления? Нужен ли badge? Как работает empty state? |
| API | Какие endpoints нужны? Нужна ли pagination? |
| Data | Какая таблица или collection хранит уведомления? Какой retention period? |
| Security | Кто может видеть уведомления? Как проверяется ownership? |
| Architecture | Где находится domain logic: controller, service или event handler? |
| Delivery | Нужны ли email, push, in-app notifications или только in-app? |
| Testing | Какие unit, integration и E2E tests доказывают корректность? |
| Operations | Нужны ли retries, dead-letter queue, observability? |
Если все это не определено, агент заполняет пробелы сам. Иногда это выглядит разумно. Но "разумно" не значит "соответствует продукту".
Типовые сбои one-shot development
Потеря требований
Агент начинает с правильной цели, но по мере работы забывает часть требований.
Например, пользователь попросил:
Не менять существующий API response.
Но во время реализации агент добавляет новое поле в response, потому что так проще для UI. Локально UI работает, но внешний API contract сломан.
Architecture drift
Без явных architecture constraints агент выбирает путь наименьшего сопротивления.
Пример:
controller -> database
вместо:
controller -> service -> repository -> database
Код может пройти тесты, но нарушить архитектуру проекта. Через несколько таких изменений система становится непредсказуемой.
Локально правильный, глобально неправильный код
Агент исправляет симптом в одном месте, но ломает инвариант системы.
Пример:
- token reset начинает работать;
- но старые sessions не инвалидируются;
- audit log не пишется;
- rate limit обходится через другой endpoint.
Каждый отдельный фрагмент выглядит нормально. Поведение продукта в целом неверное.
Unrelated changes
Большая задача создает соблазн "заодно улучшить" соседний код:
- переименовать helper;
- обновить dependency;
- переформатировать файлы;
- переписать компонент;
- изменить структуру response.
Unrelated changes опасны не потому, что они всегда плохие. Они опасны потому, что усложняют review и rollback.
Случайные зависимости
Агент может добавить новую библиотеку, хотя в проекте уже есть аналогичный механизм.
Например:
Добавляет новый date library
хотя проект уже использует стандартный helper formatDate.
Причина часто проста: агент не нашел существующий pattern или решил, что новый dependency ускорит реализацию.
Повторная реализация существующих механизмов
В большом scope агент может не заметить, что в проекте уже есть:
- email service;
- validation middleware;
- error format;
- audit logger;
- permission helper;
- API response wrapper.
В результате появляются две реализации одного механизма. Это увеличивает maintenance cost и риск расхождения поведения.
Отсутствие тестов
One-shot prompt часто формулируется через фичу, а не через проверку. Агент может написать код и кратко сказать:
Добавил тесты.
Но без traceability непонятно:
- какие acceptance criteria покрыты;
- какие edge cases пропущены;
- запускались ли тесты;
- доказывают ли они именно нужное поведение.
Context pollution
Чем дольше agent run, тем больше в контексте:
- старых планов;
- промежуточных ошибок;
- исправленных гипотез;
- длинных logs;
- частичных решений;
- устаревших assumptions.
К концу большого run агент может опираться на информацию, которая уже неактуальна.
Как one-shot ломает agent loop
Во втором уроке agent loop выглядел так:
Observe -> Plan -> Act -> Observe Result -> Continue or Stop
В one-shot режиме loop становится слишком широким:
Проблема не в том, что agent loop плохой. Проблема в том, что один loop пытается вместить слишком много разных решений.
Управляемый вариант:
Здесь каждая task имеет свой маленький loop и свой критерий завершения.
One-shot prompt vs task-based workflow
| Критерий | One-shot prompt | Task-based workflow |
|---|---|---|
| Требования | Часто скрыты в общей фразе | Зафиксированы в specification |
| Scope | Размытый | Явный allowed scope |
| Решения | Агент угадывает | Человек утверждает product/architecture decisions |
| Контекст | Большой и шумный | Минимальный и релевантный |
| Изменения | Большой diff | Маленькие проверяемые diffs |
| Тесты | Часто добавляются в конце | Связаны с acceptance criteria |
| Review | Сложный и утомительный | По одной задаче |
| Rollback | Дорогой | Можно откатить один change |
| Риск drift | Высокий | Ниже за счет boundaries |
Task-based workflow медленнее выглядит на старте, но быстрее приводит к надежному результату. Это тот случай, где немного дисциплины экономит много разборов после.
Пример: password reset
Плохой one-shot запрос:
Добавь password reset во всем приложении.
Лучше сначала извлечь решения:
Goal:
Пользователь может запросить reset password по email и установить новый пароль по одноразовому token.
Non-goals:
- Не меняем login flow.
- Не добавляем social login.
- Не меняем email provider.
Questions:
- Сколько действует token?
- Что показывать для неизвестного email?
- Инвалидировать ли старые sessions?
- Какой rate limit?
- Какие audit events нужны?
Потом сделать маленькие задачи:
T1. Описать spec и acceptance criteria.
T2. Добавить storage/model для reset token.
T3. Добавить token generation service.
T4. Добавить request reset endpoint.
T5. Добавить reset password endpoint.
T6. Добавить tests для expired/used/invalid token.
T7. Добавить UI form.
T8. Добавить E2E happy path.
Каждая задача должна иметь:
- goal;
- inputs;
- allowed scope;
- acceptance criteria;
- verification command.
Детально оформлять specification и tasks мы будем в следующих модулях. Сейчас важно увидеть направление: сначала сужаем неопределенность, потом пишем код.
Практика: сравнить два подхода
Возьмите одну фичу, например:
Система уведомлений
Сделайте два документа.
Документ 1. One-shot prompt
Напишите prompt так, как его обычно формулируют в хаотичной AI-разработке:
Сделай систему уведомлений с backend, frontend и тестами.
Затем выпишите минимум 15 скрытых решений, которые агенту придется принять.
Документ 2. Task-based outline
Разложите ту же фичу на этапы:
Specification
Design
Tasks
Tests
Review
Acceptance
Для каждой будущей task напишите один проверяемый результат.
Итоговый артефакт:
labs/01-one-shot-drift/report.md
Структура отчета:
# One-shot drift report
## Feature
## One-shot prompt
## Hidden decisions
## Likely failure modes
## Task-based alternative
## What should be verified externally
Как понять, что задача слишком большая
Задачу нужно дробить, если:
- в ней больше одного observable result;
- она требует изменений в нескольких слоях: DB, backend, frontend, tests;
- есть неутвержденные product decisions;
- непонятно, какие files are allowed to change;
- review diff будет сложно прочитать за 10-15 минут;
- невозможно назвать одну verification command;
- агент должен одновременно проектировать архитектуру и писать код.
Практическое правило курса:
Одна Agent Task = один проверяемый результат.
Это не значит, что каждая задача должна быть микроскопической. Это значит, что ее завершение должно быть доказуемым.
Типичные ошибки
Ошибка 1. "Сделай SaaS"
Задача уровня "создай продукт" не является задачей для coding agent. Это начало product discovery и specification work.
Ошибка 2. "Сначала пусть напишет, потом разберемся"
Код без требований создает ложное ощущение прогресса. Потом приходится выяснять, какое поведение агент вообще реализовал.
Ошибка 3. "Добавь тесты" в конце большого prompt
Тесты, добавленные после большой реализации, часто проверяют уже написанный код, а не исходные acceptance criteria.
Ошибка 4. Не ограничивать allowed scope
Если scope не ограничен, агент может менять соседние модули, конфиги, зависимости и UI без явной необходимости.
Ошибка 5. Путать хороший план с хорошим результатом
Agent может составить разумный план и плохо его выполнить. План нужно отдельно утверждать, а выполнение отдельно проверять.
Ошибка 6. Считать drift проблемой только слабых моделей
Drift возникает не только из-за качества модели. Он возникает из-за слишком широкого scope, шума в контексте и отсутствия промежуточных проверок.
Критерии готовности
После урока студент должен уметь:
- объяснить, что такое one-shot development и vibe coding;
- назвать минимум 5 failure modes one-shot подхода;
- найти hidden decisions внутри большой фичи;
- объяснить разницу между большим prompt и task graph;
- сформулировать правило "one Agent Task - one verifiable outcome";
- подготовить короткий one-shot drift report.
Краткое резюме
One-shot development плохо масштабируется, потому что большая фича содержит слишком много скрытых решений. Когда эти решения не вынесены в specification, design и tasks, агент начинает угадывать, терять требования, нарушать архитектуру, менять лишние файлы и писать непроверяемый код.
Большой prompt не решает проблему большой задачи. Он часто только увеличивает context pollution.
Надежный подход:
Big feature
↓
Clarify behavior
↓
Write specification
↓
Design constraints
↓
Build task graph
↓
Run small agent tasks
↓
Verify each result
Это и есть мост к Specification-Driven Development.
Вопросы для самопроверки
- Почему фраза "сделай password reset" содержит много скрытых решений?
- Чем local correctness отличается от global correctness?
- Почему unrelated changes особенно опасны в AI-generated diff?
- Как context pollution влияет на длинный agent run?
- Что означает правило "одна Agent Task = один проверяемый результат"?
Что дальше
Модуль I дал базовую рамку: LLM ограничена контекстом, coding agent работает через loop, а one-shot development теряет управляемость на больших задачах.
В следующем модуле мы перейдем к Specification-Driven Development. Первый шаг - научиться писать specification как observable behavior: не "сделай хороший authentication", а конкретные требования, edge cases и acceptance criteria, которые можно проверить.