Почему декомпозиция критична для AI
Вот факт, подтверждённый практикой тысяч разработчиков, использующих AI-агентов: чем меньше задача, тем выше вероятность успеха.
Размер задачи vs. вероятность корректного результата:
Атомарная (20-50 строк) ██████████████████ ~95%
Малая (50-100 строк) ████████████████ ~85%
Средняя (100-300 строк) ██████████ ~60%
Большая (300-1000 строк) █████ ~30%
Огромная (1000+ строк) ██ ~10%
Это не теоретические цифры -- это наблюдаемая закономерность. Причины:
-
Контекстное окно. Чем больше задача, тем больше контекста нужно удерживать. AI теряет фокус на длинных задачах.
-
Поверхность ошибки. Чем больше кода генерируется, тем выше вероятность хотя бы одной ошибки, которая каскадно ломает всё остальное.
-
Верификация. Маленькую задачу можно проверить юнит-тестом за секунды. Большую -- только интеграционным тестированием, которое может занять минуты.
-
Откат. Если маленькая задача провалилась --
git checkout -- .и пробуем снова. Если большая -- нужно разбираться, что спасать.
Принцип: Декомпозиция -- это не "разбить фичу на части". Это инженерная дисциплина создания задач, оптимальных для автономного выполнения.
Критерии атомарной задачи
Атомарная задача -- это наименьшая единица работы, которую AI-агент может выполнить за одну итерацию Ralph Loop. Хорошая атомарная задача отвечает пяти критериям:
1. Single Responsibility (Единственная ответственность)
Задача затрагивает один файл, одну функцию, одну заботу.
# Плохо — множественная ответственность
- [ ] Создать User entity, UserRepository и UserService
# Хорошо — единственная ответственность
- [ ] Создать User entity (src/Entity/User.php)
- [ ] Создать UserRepository (src/Repository/UserRepository.php)
- [ ] Создать UserService (src/Service/UserService.php)
2. Clear Input/Output (Чёткий вход/выход)
AI-агент должен точно знать: что у него есть на входе и что он должен произвести на выходе.
# Плохо — нечёткий вход/выход
- [ ] Добавить валидацию пользователя
# Хорошо — чёткий контракт
- [ ] Создать CreateUserRequest DTO
- Input: email (string, email format), password (string, min 8 chars), name (string, max 255)
- Output: DTO class with Symfony Validator attributes
- Location: src/DTO/CreateUserRequest.php
3. Verifiable (Верифицируемость)
Должен существовать автоматический способ проверить, выполнена ли задача.
| Тип верификации | Пример | Скорость |
|---|---|---|
| Компиляция | go build ./... проходит |
< 1 сек |
| Статический анализ | PHPStan level 9 чистый | 2-5 сек |
| Юнит-тест | Конкретный тест проходит | < 1 сек |
| Линтер | ESLint без ошибок | 1-3 сек |
# Плохо — неверифицируемая задача
- [ ] Код должен быть хорошо структурирован
# Хорошо — верифицируемая задача
- [ ] Создать UserService.register() — после выполнения:
- [ ] phpstan analyse проходит на level 9
- [ ] UserServiceTest::testRegisterSuccess() проходит
- [ ] UserServiceTest::testRegisterDuplicateEmail() проходит
4. Time-Bounded (Ограниченность по времени)
Задача должна завершаться за 1-5 минут (одна итерация цикла). Если задача занимает дольше -- она недостаточно декомпозирована.
Время на задачу:
< 1 мин: Слишком мелко (добавить import, переименовать переменную)
1-3 мин: Идеально (создать класс, написать метод + тест)
3-5 мин: Приемлемо (реализовать сервис с несколькими методами)
5-10 мин: Слишком крупно — нужно разбить
> 10 мин: Опасно — агент, скорее всего, запутается
5. Context-Minimal (Минимальный контекст)
Задача не должна требовать чтения всей кодовой базы. Агент должен понять задачу, прочитав 2-3 файла максимум.
# Плохо — требует понимания всей системы
- [ ] Оптимизировать workflow обработки заказа
# Хорошо — требует минимального контекста
- [ ] Добавить метод OrderRepository::findByStatus(OrderStatus $status): array
- Context: src/Entity/Order.php (для понимания сущности)
- Pattern: src/Repository/ProductRepository.php (для паттерна)
Стратегии декомпозиции
Вертикальная нарезка (Vertical Slicing)
Разбиение по слоям архитектуры -- от данных до API:
Feature: Order API
Entity → Repository → Service → Controller → Test
────── ────────── ─────── ────────── ────
Order.php → OrderRepo.php → OrderSvc.php → OrderCtrl.php → OrderSvcTest.php
OrderCtrlTest.php
Порядок выполнения строго от нижних слоёв к верхним:
# Vertical Slicing — Order API
## Layer 1: Data
- [ ] Create OrderStatus enum
- [ ] Create Order entity with fields and relationships
- [ ] Create OrderItem entity
- [ ] Create Doctrine migration
## Layer 2: Repository
- [ ] Create OrderRepository with basic CRUD
- [ ] Add OrderRepository::findByUser()
- [ ] Add OrderRepository::findByIdempotencyKey()
## Layer 3: Service
- [ ] Create CreateOrderRequest DTO
- [ ] Create OrderResponse DTO
- [ ] Create OrderService::create()
- [ ] Create OrderService::getById()
- [ ] Create OrderService::listByUser()
## Layer 4: API
- [ ] Create OrderController::create() — POST
- [ ] Create OrderController::show() — GET /{id}
- [ ] Create OrderController::index() — GET
## Layer 5: Tests
- [ ] Unit tests for OrderService
- [ ] Integration tests for OrderController
Когда использовать: для новых фич, CRUD API, типовых модулей.
Горизонтальная нарезка (Horizontal Slicing)
Разбиение по функциональным аспектам:
Feature: User Registration
CRUD → Validation → Authorization → Caching → Events
──── ────────── ───────────── ─────── ──────
Base logic Email format Role checks Redis cache Email sent
Password rules Rate limiting Audit log
Uniqueness
# Horizontal Slicing — User Registration
## Aspect 1: Basic CRUD
- [ ] Create User entity
- [ ] Create UserRepository
- [ ] Create UserService.register() — basic version (no validation)
## Aspect 2: Validation
- [ ] Add email format validation
- [ ] Add password strength validation (min 8, uppercase, digit)
- [ ] Add email uniqueness check
## Aspect 3: Security
- [ ] Add password hashing (bcrypt)
- [ ] Add rate limiting (max 5 registrations per IP per hour)
## Aspect 4: Side Effects
- [ ] Dispatch UserRegisteredEvent
- [ ] Add email verification handler
- [ ] Add audit log entry
## Aspect 5: Caching
- [ ] Cache user by ID in Redis
- [ ] Cache user by email in Redis
- [ ] Invalidate cache on user update
Когда использовать: для сложных фич с перекрёстными задачами (security, caching, logging).
Test-First нарезка (Test-First Slicing)
Каждая задача -- это пара: тест + реализация.
# Test-First Slicing — Calculator Service
- [ ] Write test: add(2, 3) returns 5 → Implement add()
- [ ] Write test: add(-1, 1) returns 0 → Handle negative numbers
- [ ] Write test: subtract(5, 3) returns 2 → Implement subtract()
- [ ] Write test: divide(10, 2) returns 5 → Implement divide()
- [ ] Write test: divide(10, 0) throws DivisionByZeroException → Handle edge case
- [ ] Write test: multiply large numbers doesn't overflow → Add overflow protection
Когда использовать: для бизнес-логики, алгоритмов, расчётов -- где корректность критична.
Процесс декомпозиции
Пошаговый алгоритм декомпозиции фичи:
Шаг 1: Начните со спецификации
# Feature Spec: Product Reviews
Users can:
- Write reviews for products they purchased
- Rate products (1-5 stars)
- Edit their reviews
- See all reviews for a product
- See average rating for a product
Шаг 2: Определите все компоненты
# Components needed:
## Entities
- Review (id, user_id, product_id, rating, text, created_at, updated_at)
## Repositories
- ReviewRepository (create, update, findByProduct, findByUserAndProduct, avgRating)
## Services
- ReviewService (createReview, updateReview, getProductReviews, getAverageRating)
## DTOs
- CreateReviewRequest (product_id, rating, text)
- UpdateReviewRequest (rating, text)
- ReviewResponse (id, user, rating, text, created_at)
- ProductReviewsResponse (reviews[], average_rating, total_count)
## Controllers
- ReviewController (create, update, list)
## Tests
- ReviewServiceTest
- ReviewControllerTest
Шаг 3: Постройте граф зависимостей
Review entity
└── ReviewRepository
└── ReviewService
├── CreateReviewRequest DTO
├── UpdateReviewRequest DTO
├── ReviewResponse DTO
└── ReviewController
└── Integration tests
Зависимости идут сверху вниз.
Реализация идёт снизу вверх.
Шаг 4: Упорядочьте по зависимостям
Критическое правило: задача не может быть выполнена, если её зависимости не готовы.
# Ordered tasks (dependency-first):
1. Review entity (no dependencies)
2. CreateReviewRequest DTO (depends on: Review entity for validation rules)
3. ReviewRepository (depends on: Review entity)
4. ReviewResponse DTO (depends on: Review entity)
5. ReviewService.createReview (depends on: Repository, DTOs)
6. ReviewService.updateReview (depends on: Repository, DTOs)
7. ReviewService.getProductReviews (depends on: Repository, DTOs)
8. ReviewController.create (depends on: Service, DTOs)
9. ReviewController.update (depends on: Service, DTOs)
10. ReviewController.list (depends on: Service, DTOs)
11. Unit tests for ReviewService (depends on: Service)
12. Integration tests for API (depends on: Controller)
Шаг 5: Разбейте на атомарные задачи
# Final atomic task list:
- [ ] Create Review entity with fields: id (UUID v7), user_id, product_id, rating (1-5), text, created_at, updated_at
- [ ] Create Doctrine migration for reviews table with indexes on (product_id) and (user_id, product_id) unique
- [ ] Create CreateReviewRequest DTO with validation: rating (1-5), text (10-5000 chars), product_id (UUID)
- [ ] Create UpdateReviewRequest DTO with validation: rating (1-5, optional), text (10-5000 chars, optional)
- [ ] Create ReviewRepository with save() and flush() methods
- [ ] Add ReviewRepository::findByProduct(productId, cursor, limit) with cursor pagination
- [ ] Add ReviewRepository::findByUserAndProduct(userId, productId) for uniqueness check
- [ ] Add ReviewRepository::getAverageRating(productId) returning float
- [ ] Create ReviewResponse DTO with fromEntity() static factory method
- [ ] Create ProductReviewsResponse DTO with reviews array, average_rating, total_count
- [ ] Create ReviewService::createReview(userId, CreateReviewRequest) with ownership check
- [ ] Create ReviewService::updateReview(userId, reviewId, UpdateReviewRequest) with ownership check
- [ ] Create ReviewService::getProductReviews(productId, cursor, limit) returning ProductReviewsResponse
- [ ] Create ReviewController::create() — POST /api/v1/products/{id}/reviews
- [ ] Create ReviewController::update() — PUT /api/v1/reviews/{id}
- [ ] Create ReviewController::list() — GET /api/v1/products/{id}/reviews
- [ ] Unit test: ReviewService::createReview — happy path
- [ ] Unit test: ReviewService::createReview — duplicate review throws exception
- [ ] Unit test: ReviewService::createReview — invalid product throws exception
- [ ] Unit test: ReviewService::updateReview — not owner throws exception
- [ ] Integration test: POST /api/v1/products/{id}/reviews — creates review
- [ ] Integration test: GET /api/v1/products/{id}/reviews — returns paginated list
Шаг 6: Добавьте критерии верификации
Каждая задача должна иметь проверяемый критерий:
- [ ] Create Review entity
Verify: `phpstan analyse src/Entity/Review.php` — 0 errors
Verify: `php bin/phpunit --filter=ReviewTest` — if test exists, passes
- [ ] Create ReviewService::createReview()
Verify: `phpstan analyse src/Service/ReviewService.php` — 0 errors
Verify: `php bin/phpunit --filter=testCreateReviewSuccess` — passes
Verify: `php bin/phpunit --filter=testCreateReviewDuplicate` — passes
Типичные ошибки декомпозиции
Ошибка 1: Задачи слишком крупные
# Плохо
- [ ] Implement user management module
# Почему плохо: это 20+ файлов, 500+ строк, множество решений.
# AI-агент потеряет фокус к середине и начнёт генерировать некачественный код.
# Хорошо: разбить на 15-20 атомарных задач
Ошибка 2: Задачи слишком мелкие
# Плохо
- [ ] Add import for UserRepository in UserService
- [ ] Add private property $userRepository in UserService
- [ ] Add constructor parameter for UserRepository
- [ ] Add type hint for constructor parameter
# Почему плохо: каждая из этих задач не самодостаточна. Агент должен
# прочитать контекст, загрузить файлы, всё это ради одной строки. Это
# расточительно по отношению к контекстному окну и API-стоимости.
# Хорошо: объединить в одну задачу
- [ ] Create UserService with constructor injection of UserRepository
Ошибка 3: Нарушение зависимостей
# Плохо — задача 3 зависит от задачи 5
- [ ] 1. Create User entity
- [ ] 2. Create UserRepository
- [ ] 3. Create UserController (нужен UserService, которого ещё нет!)
- [ ] 4. Create UserDTO
- [ ] 5. Create UserService
# Хорошо — порядок соответствует зависимостям
- [ ] 1. Create User entity
- [ ] 2. Create UserRepository
- [ ] 3. Create UserDTO
- [ ] 4. Create UserService (зависит от Repository и DTO — они готовы)
- [ ] 5. Create UserController (зависит от Service — он готов)
Ошибка 4: Неясная верификация
# Плохо
- [ ] Make the code better
- [ ] Improve performance
- [ ] Fix the bug
# Почему плохо: как агент поймёт, что "better"? Что значит "improved"?
# Какой именно "bug"?
# Хорошо
- [ ] Refactor UserService.register() — extract email validation into EmailValidator class
Verify: phpstan clean, existing tests pass
- [ ] Add index on users.email column
Verify: migration applies, query EXPLAIN shows index scan
- [ ] Fix: UserService.register() doesn't check email uniqueness — add check
Verify: testRegisterDuplicateEmailThrowsException passes
Руководство по размеру задач
Идеальная задача
## Task ID: REV-005
## Title: Create ReviewService.createReview()
### Context
- Review entity: src/Entity/Review.php (read for fields/types)
- ReviewRepository: src/Repository/ReviewRepository.php (read for available methods)
- Pattern reference: src/Service/ProductService.php (follow same structure)
### Requirements
1. Method signature: createReview(UuidV7 $userId, CreateReviewRequest $dto): ReviewResponse
2. Check product exists (throw ProductNotFoundException if not)
3. Check no existing review from same user for same product (throw DuplicateReviewException)
4. Create Review entity, set all fields
5. Save via repository
6. Return ReviewResponse::fromEntity($review)
### Verification
- [ ] PHPStan level 9 clean on src/Service/ReviewService.php
- [ ] Unit test: testCreateReviewSuccess passes
- [ ] Unit test: testCreateReviewDuplicateThrowsException passes
- [ ] Unit test: testCreateReviewInvalidProductThrowsException passes
### Dependencies
- REV-001 (Review entity) ✅
- REV-002 (CreateReviewRequest DTO) ✅
- REV-003 (ReviewRepository) ✅
- REV-004 (ReviewResponse DTO) ✅
### Estimated Size
~40-60 lines of production code + ~80 lines of test code
Размерная шкала
| Размер | Строки кода | Примеры | Подходит для AI? |
|---|---|---|---|
| XS | 5-20 | Enum, DTO, константы | Слишком мелко для отдельной итерации |
| S | 20-50 | Простой метод, простой тест | Идеально |
| M | 50-100 | Сервис с 1-2 методами + тесты | Идеально |
| L | 100-200 | Сервис с 3-5 методами + тесты | Приемлемо (upper bound) |
| XL | 200-500 | Целый модуль | Разбить обязательно |
| XXL | 500+ | Целая фича | Разбить немедленно |
Оптимальный размер: S-M (20-100 строк). Это "sweet spot", где AI-агент работает наиболее эффективно.
Инструменты для управления задачами
Markdown-чеклисты (простейший подход)
# fix_plan.md — all you need for small projects
- [x] Task 1
- [x] Task 2
- [ ] Task 3 ← agent picks this one
- [ ] Task 4
Плюсы: простота, агент легко парсит, версионируется в git. Минусы: нет метаданных, нет приоритетов, нет назначений.
GitHub Issues (командный подход)
# Create issues from fix plan
gh issue create --title "REV-001: Create Review entity" \
--body "Create Review entity with fields..." \
--label "ralph-loop,priority-high"
Плюсы: отслеживание, метки, назначения, интеграция с PR. Минусы: сложнее для AI-агента (нужен API-доступ).
Linear/Jira (enterprise-подход)
Для больших проектов с несколькими потоками работы.
Плюсы: полное управление проектом, аналитика. Минусы: оверхед для Ralph Loop, замедляет итерации.
Рекомендация: Для Ralph Loop используйте markdown-чеклисты. Они минимальны, быстры и легко парсятся AI-агентом. Переходите на Issues/Linear только если у вас несколько параллельных агентов или команда из 3+ человек.
Выводы
-
Атомарность -- это не цель, а средство. Задачи делаются маленькими не ради маленькости, а ради высокой вероятности успеха.
-
5 критериев: единственная ответственность, чёткий вход/выход, верифицируемость, временные рамки, минимальный контекст.
-
Порядок зависимостей обязателен. Сущности перед репозиториями, репозитории перед сервисами, сервисы перед контроллерами.
-
20-100 строк -- sweet spot. Меньше -- расточительно. Больше -- рискованно.
-
Верификация = автоматические проверки. Если задачу нельзя проверить автоматически, она плохо декомпозирована.
-
Fix plan -- живой документ. Агент обновляет его после каждой итерации, отмечая выполненные задачи и добавляя новые при необходимости.