Функциональность как first-class artifact

Чего не видит агент в хорошо организованном проекте: пробел между архитектурой и тикетами

25 апреля 2026

В зрелой команде архитектурная сторона обычно размечена: модули, bounded contexts, ADR, arc42-секции, OpenAPI, repo-map, AGENTS.md. Эта разметка отвечает на вопрос «как устроен код» - и работает: агент по символу попадает в файл, по эндпоинту видит контракт, по ADR понимает, почему так. Но агенту, которому ставят задачу «поправь баг на онбординге», нужен другой ответ - «что приложение умеет с точки зрения пользователя и где это в коде». Этот функциональный слой почти ни у кого не оформлен явно: он рассыпан по Jira-эпикам, Confluence-PRD, реестру feature-флагов, головам PM. Прицельного публичного решения (machine-readable feature catalog × CJM-индекс × связан с кодом × живой через CI) нет - соседние подходы закрывают только части. Статья описывает, почему пробел существенен, картирует то, что уже есть, и предлагает конкретную структуру - Operation → Job → Journey + Feature Registry + слой проблем (pain_points / constraints / unhappy_paths) - валидированную в production у одного B2B SaaS со сложной доменной моделью.

Где уже хорошо: техническая сторона размечена

Зрелая инженерная команда обычно имеет несколько слоёв документации, отвечающих на технические вопросы:

Этот стек работает. Агент по упоминанию класса попадает в файл. По эндпоинту находит контракт. По ADR понимает, почему запрос асинхронный, а не синхронный. Эта часть - решённая задача, и большая часть индустрии уже её решает теми или иными комбинациями инструментов.

Чего нет: функциональность как наблюдаемая поверхность

Проблема в том, что вся перечисленная разметка отвечает на вопрос «как устроен код». Она не отвечает на вопрос «что приложение умеет с точки зрения пользователя».

«Фича» в этой статье - не модуль и не endpoint. Фича - это наблюдаемая возможность пользователя что-то сделать в продукте: онбординг, восстановление пароля, экспорт CSV, отправка приглашения, эскалация заявки в саппорт, оплата по подписке. Единица, которую видит и обсуждает пользователь, продакт-менеджер, support-инженер, иногда юрист. Не unit of code, а unit of capability.

В типичном проекте этот слой размечен неявно. Знание о фичах живёт в:

Машиночитаемой карты «что приложение умеет → где это в коде» обычно нет. И даже когда часть знания формализована (например, есть PRD в Notion), она не связана с кодом ссылками, которые агент мог бы пройти.

Это и есть пробел. Не техническая сторона, не архитектура, не контракты - именно функциональная поверхность.

Почему агенту это критично: четыре класса провалов

Intent gap

Задачи приходят в терминах пользователя, а не в терминах сервисов. «Поправь баг на онбординге», «добавь поле в экспорт», «клиент не получает email после регистрации». Агент должен сам сообразить, в каких файлах живёт «онбординг» как фича.

Хороший DDD не помогает: bounded context Auth содержит и регистрацию, и login, и password reset, и MFA, и SSO - десятки независимых пользовательских capability. Знание о том, что онбординг = регистрация + welcome-email + первичная настройка профиля + опциональный туториал, не зафиксировано нигде. Агент это «угадывает» по grep'у и пропускает шаги.

Boundary blur

Без явных границ фичи правка либо узкая, либо размазанная. Агент правит ровно тот файл, на который указал пользователь, но забыл соседний endpoint, который тоже относится к фиче. Или наоборот - вошёл в чужую фичу под предлогом рефакторинга и сломал кусок, не имеющий отношения к задаче.

Модуль не равен фиче. Bounded context UserProfile может содержать «фичу аватаров», «фичу custom fields», «фичу импорта из LDAP» - все три имеют разные roadmap, owners, lifecycle и тесты. Без явной разметки границ фичи агент трактует «правка UserProfile» как «правка любого файла в этом модуле».

Cross-feature blindness

Две фичи делят одно событие, одну таблицу или один queue. Это типично: «новый пользователь» - событие, на которое подписаны и welcome-email, и провижининг workspace, и outbound-аналитика, и invite-flow для команды. Но это нигде не записано как первoклассный факт. Агент правит handler welcome-email и неосознанно ломает provisioning, потому что они слушают одно и то же событие с одинаковым subscription pattern.

DDD context map иногда фиксирует upstream/downstream между контекстами, но не на уровне фич. ADR фиксирует решение «использовать event-driven архитектуру» - но не «эти три фичи разделяют это событие». Без явных related_features агент не видит коллизий.

Lifecycle blindness

Фича deprecated, за флагом, в migration mode, замороженная до Q3, доступна только enterprise-tier клиентам. Это статусы, которые меняют поведение разработки: «не правь это, мы это удаляем», «это под флагом, новый код не должен это вызывать», «миграция в процессе, не трогай старый code path до завершения переноса event ledger».

Эта информация почти всегда живёт в головах. Агент пишет код против фичи, которая через две недели будет удалена. Или фиксит баг в старом code path, который уже за миграционным флагом ведёт в новую реализацию. Без явного lifecycle-поля у feature-узла агент работает в темноте.

Карта соседних референсов

Прежде чем предлагать решение, полезно зафиксировать, что уже есть, и где у каждого подхода граница.

Подход Что закрывает Чего не делает
Meta-repo pattern (seylox, март 2026) Cross-repo навигация: AGENTS.md + repos.yaml + workflows/ + active-work/ Сам автор пишет: «фокус на внутренней архитектуре, не на user-facing capabilities»
deliberate.codes spec (2026) Качество одного спека: Purpose / Requirements / Scenarios, RFC 2119, явные SHALL NOT Один спек, не inventory; нет journey, нет связности
Spec Kit, OpenSpec, Kiro, BMAD Spec authoring в моменте создания фичи; EARS-нотация в Kiro Точечные спеки, не постоянный feature catalog; брошенные после implementation
Backstage Software Catalog Каталог сервисов, API, ownership, dependency graph Сервисы и компоненты, не user-facing capabilities
Backstage AIContext (RFC #33575) Попытка добавить AI-context как entity в catalog Скиллы и rules, не функциональная карта; в обсуждении
Feature-flag manifests (OpenFeature, Reflag, Cloudflare Flagship) Live-toggles с machine-readable manifest; пример - Duolingo flag-removal agent In-flight subset, не полный inventory; вне флагов знания нет
Journey-mapping AI-агенты (A. Grippo и др., 2026) Schema-first journey-data: stages, actions, touchpoints, pains, opportunities UX-исследование, не привязано к коду; одноразовый артефакт
AGENTS.md, Memory Bank, SKILL.md Корневой протокол поведения, скиллы как пакеты, persistent context Корневой / инфраструктурный уровень, не feature catalog
Codified Context Infrastructure (arXiv:2602.20478) Трёхъярусная память на 108K LOC, 283 сессии: hot constitution, domain specialists, cold knowledge Cold-memory организована по техническим темам, не по user-facing capabilities
DDD bounded contexts, arc42, C4 Архитектурная декомпозиция Один context = десятки фич; гранулярность не та
User Story Mapping (Patton, 2014) Visualization journey как backbone + walking skeleton Стикеры на доске; не machine-readable, не привязаны к коду
BDD / Specification by Example (Adzic, 2011) Gherkin как living documentation Acceptance criteria без feature inventory и без journey-индекса
Capability surfaces (Schema.org BuyAction, Vercel Agent Readability) Декларация capability для AI-агентов в вебе/коммерции Другая среда (web), не код проекта

Никто целиком не закрывает «machine-readable feature catalog × journey-индекс × связан с кодом × живой через CI». Это значит:

Возможный подход: Operation → Job → Journey + Feature Registry

Один из подходов, который работает в продакшене у B2B SaaS со сложной доменной моделью (учётная и налоговая практика для accounting-фирм, десятки interconnected workflows, два типа пользователей - firm-сторона и client-сторона - с разными journeys, сезонные пики нагрузки, регуляторные ограничения), - трёхуровневая иерархия + отдельный реестр фич + слой проблем.

Структура называется Business Operations Hierarchy. Текущее состояние - 17 operations, 30 jobs, happy paths размечены для всех; pain_points и метрики добавляются итеративно. Применяется как input context для AI при написании PRD, генерации продуктовых гипотез, оценке impact фич, планировании research, написании release notes, онбординге новых членов команды.

Уровень 1: Operation

Operation - это repeatable unit of work, который команда (или клиент команды) выполняет в продукте. Имеет понятный trigger, участников и outcome. Это не фича, не модуль и не endpoint - это повторяющаяся единица бизнес-активности.

Примеры из анонимного B2B SaaS: «intake документов от возвращающегося клиента», «проверка собранных данных перед началом работы», «выставление подписки и управление seat-ами», «балансировка нагрузки между членами команды».

Поля Operation:

Тип operation: service-specific (привязана к конкретной service-line) или cross-cutting (работает поверх всей платформы).

Уровень 2: Job (JTBD)

Job - это конкретная цель, которой пользователь пытается достичь в рамках operation. Формулируется в JTBD-нотации (Christensen, 2016; Ulwick, 2016): «When I [situation], I want to [action], so that [outcome]».

Пример: «When I begin intake for a returning tax client, I want to determine which tax documents are still needed, so I can request the right items upfront and move the client into preparation without unnecessary delay or back-and-forth».

Поля Job:

Уровень 3: Journey

Journey - это ordered sequence шагов от trigger до outcome. Каждый шаг помечен actor-меткой: [Firm], [Doer], [Manager], [Owner], [Client], [System]. Это и есть тот самый «декомпозированный CJM», который запускает агента в коде.

Пример (фрагмент):

[Firm] Review prior-year client context (last year's return, account tags, notes)
[Firm] Identify which documents are needed this year
[Firm] Build the document checklist - manually or using AI from prior-year return PDF
[Firm] Send organizer with checklist to the client
[Client] Receive notification (email / push / portal to-do)
[Client] Open organizer, fill out questionnaire
[Client] Upload documents and match them to checklist items
[Client] Submit organizer
[Firm] Review submitted organizer; verify uploaded documents
[Firm] If documents are missing - send a Client Request for the specific items
[Client] Upload missing documents
[Firm] Confirm all documents received; move job to next pipeline stage

Каждый шаг - точка, к которой можно подвесить связь с кодом, тестом, флагом, ADR. Когда задача формулируется как «баг на шаге 7 онбординга для возвращающегося клиента», агент перестаёт угадывать.

Feature Registry: фичи как переиспользуемые capability

Здесь принципиально - фичи живут в отдельном реестре, и Jobs ссылаются на них, не дублируя описание. Одна фича (например, «Document Checklist», «Pipeline Automations», «Client Requests») переиспользуется в десятках jobs, и дублировать её описание - значит обречь систему на drift.

Detail Feature Registry разбирается в следующей секции.

Слой проблем

Главное отличие этой структуры от обычного «journey-документа» - не happy path, а слой проблем: pain_points, constraints, unhappy_paths. Без него агент генерирует generic-гипотезы («давайте автоматизируем шаг 3»). С ним - агент попадает в реальные боли пользователей.

Метаданные приоритизации (volume, priority_tier, success_metric) дают агенту основания не «починить всё подряд», а сосредоточиться на P0-операциях с высоким volume. Это превращает feature catalog из справочника в инструмент prioritization.

Принципы living document

Принципы применимы к любой реализации, не только к описанной структуре:

  1. Living, not snapshot. Документ обновляется при каждом значимом изменении продукта. Update-триггеры: launch новой фичи, новая service-line, research-результаты, изменения бизнес-модели, ежеквартальный review.
  2. One owner, many contributors. Один владелец - человек из research-команды, отвечающий за консистентность; contributors - PMs (правят jobs/journeys при шиппинге фич), researchers (добавляют pain_points/unhappy_paths), support (приносят топ pain_points из тикетов), data analysts (volume, success_metric).
  3. Depth over breadth. 10 jobs с полным набором полей (pain_points, constraints, unhappy_paths, metrics) ценнее 50 jobs с happy-path-only.
  4. Review как для кода. Минорные правки - self-merge с уведомлением owner; новый Job или Operation - review от owner + PM из релевантной области; структурные изменения - review от owner + product lead.
  5. Use as input context for AI, not as a final artifact. Документ не заменяет PRD, research-plans или roadmap. Он - context, который делает все эти артефакты лучше.
  6. Не дублировать то, что живёт в других системах. Каталог содержит references на features, тикеты, research-отчёты, не их копии.

Что класть в feature-узел Feature Registry

Operation → Job → Journey - функциональная карта. Feature Registry - то, что связывает её с кодом.

Узел Feature Registry - YAML-документ (markdown с frontmatter тоже подходит, но YAML агенты парсят надёжнее) со следующими полями. Каждое поле обосновано через конкретный класс агентских вопросов, которые оно закрывает.

Пример узла (полный):

id: document-checklist
name: Document Checklist
purpose: |
  Интерактивный список документов, который firm запрашивает у client для конкретного engagement.
  Client видит список в портале/мобильном приложении, помечает items как N/A или uploads
  документ; firm видит progress в реальном времени.

lifecycle: active

triggers:
  - "[Firm] sends organizer with checklist to client"
  - "[Firm] sends standalone Client Request"

primary_flow:
  - given: client receives notification with checklist
    when: client opens checklist in portal
    then: each item shows status (pending / uploaded / N/A)

acceptance_criteria:
  - id: ac-1
    given: client uploads document via mobile app
    when: document is matched to checklist item
    then: item moves to "uploaded" status and firm sees notification
  - id: ac-2
    given: client marks item as N/A
    when: firm reviews the checklist
    then: N/A items show explanation if provided

files_likely_affected:
  - app/services/document_checklist/**
  - app/models/checklist_item.rb
  - app/views/clients/checklists/*.erb
  - spec/features/document_checklist_spec.rb

contracts:
  api:
    - POST /api/v2/checklists/:id/items/:item_id/upload
    - PATCH /api/v2/checklists/:id/items/:item_id/mark_na
  events_published:
    - checklist.item_uploaded
    - checklist.completed
  events_consumed:
    - documents.received
    - clients.invited

invariants:
  - completed checklist cannot be modified by client without firm reopening it
  - item marked as "uploaded" must reference a valid Document record

failure_modes:
  - mode: client uploads photo instead of PDF
    impact: downstream OCR fails
    strategy: validate MIME-type at upload, prompt for re-upload
  - mode: pipeline automation fires twice (idempotency violation)
    impact: duplicate notifications to client
    strategy: idempotency key on automation execution

concurrency_risks:
  - feature: pipeline_automations
    risk: automation modifies checklist while client is editing
  - feature: client_requests
    risk: duplicate request for same document via different surface

bounded_context_ref: documents
adr_refs:
  - ADR-0017-checklist-storage-model
  - ADR-0024-event-driven-notifications
flag_refs:
  - checklist_v2_layout (rollout)
related_features:
  - organizers
  - client_requests
  - pipeline_automations

owners:
  team: documents-platform
  slack: "#documents-team"

metrics:
  - dashboard: grafana://document-checklist/overview
  - kpi: completion_rate_within_7_days

Хранение - YAML или Markdown с frontmatter. Markdown полезен, если хочется в один файл совместить машиночитаемый header и человеческое описание (этот же приём использует Anthropic SKILL.md - frontmatter + body). Структурированную часть агенты парсят через frontmatter; описание - читают как контекст.

Где живёт и как сшивается с DDD, arc42, ADR

Функциональный слой (Operations + Jobs + Feature Registry) - не самостоятельный документ, который заменяет архитектурную документацию. Он - connector, ссылающийся:

Функциональный слой не дублирует архитектуру; он индексирует её и репозиторий по user-facing координатам.

Где физически хранить

Вариант A: рядом с кодом, в репозитории.

/operations/
  tax-prep/
    intake/
      job-1.yaml          # JTBD + journey + pain_points
      job-2.yaml
    review/
      job-1.yaml
/features/
  document-checklist.yaml
  client-requests.yaml
  organizers.yaml

Плюсы: близость к коду, одно PR-ревью на изменение фичи и узла, schema-валидация в CI. Минусы: разные команды могут пройти мимо, если у них собственная репозиторная структура.

Вариант B: отдельный репозиторий-каталог.

Плюсы: один источник истины поверх многих репозиториев, проще CI и валидация. Минусы: drift между каталогом и кодом - физическая дистанция увеличивает шансы рассинхрона.

Вариант C: Backstage AIContext (RFC #33575).

Плюсы: интеграция с уже существующим developer portal (если он есть), ownership/lifecycle бесплатно. Минусы: lock-in, RFC всё ещё в обсуждении, схема не оптимизирована под user-facing capabilities.

Рекомендация по умолчанию - вариант A, с automated mirror в developer portal через CI, если portal используется. Близость к коду - главное, что защищает от drift.

Слой качества: ATAM-utility tree поверх journey

Functional layer отвечает на «что делает». Quality layer отвечает на «насколько хорошо это нужно делать». Без quality scenarios агент не видит, какие правки опасны.

ATAM (Architecture Tradeoff Analysis Method, Bass / Klein / Kazman, CMU/SEI-2000-TR-004) предлагает строить utility tree:

quality
├── performance
│   ├── latency
│   │   └── scenario: онбординг шаг 7 (upload) - p95 < 3s
│   └── throughput
├── security
│   ├── data_isolation
│   │   └── scenario: client A не видит документы client B
│   └── identity
└── availability
    └── scenario: intake-flow доступен в 99.9% случаев

Сценарии привязаны к конкретным шагам journey, не к модулям. Зачем это агенту?

Когда агент правит шаг 7 онбординга, он видит, что это sensitivity point по latency p95 < 3s. Это меняет его поведение: он не предлагает синхронный сетевой вызов в этом code path, не добавляет блокирующий external API call без timeout, не вводит N+1 query в горячий путь.

Минимальный артефакт - YAML с quality scenarios:

quality_scenarios:
  - id: qs-onboarding-latency
    quality: performance.latency
    scenario: |
      Когда client загружает документ на шаге 7 онбординга,
      от click до confirmation должно проходить <3s p95
    sensitivity_for:
      - operation: tax-prep.intake
        job: job-2
        step: 7
    sources:
      - dashboard: grafana://onboarding/upload-latency
      - SLO: docs/slos/onboarding.md

  - id: qs-document-isolation
    quality: security.data_isolation
    scenario: |
      Документы client A не должны быть доступны client B
      ни через API, ни через portal, ни через no-login link
    sensitivity_for:
      - feature: document-checklist
      - feature: client-requests
      - feature: no-login-links

Полный utility tree - амбициозно. Минимум, который реально работает: 5-7 сценариев на самые critical journey-шаги. Большинство правок не затрагивает quality-границ; те, которые затрагивают, должны явно столкнуться с этим артефактом.

Как держать слой живым

Документация дрейфует. Без механик поддержания feature catalog умрёт за квартал и станет хуже, чем его отсутствие, - агент будет генерировать ответы по устаревшим данным.

Пять механик:

1. Schema-валидация в CI

JSON Schema на формат YAML-узлов (Operation, Job, Feature) и проверка в CI. PR не проходит, если узел сломан или не имеет обязательных полей. Это превращает feature catalog из «wiki» в код-уровень артефакт.

2. Drift-detector

Cron / CI-job, который раз в неделю сравнивает узлы Feature Registry с реальностью:

Детектор не блокирует, он создаёт issues в трекере для owner.

3. PR-чеклист или агентский pre-commit

В definition of done для feature launches: «обновить узел Feature Registry». Можно автоматизировать: pre-commit hook проверяет, что diff в app/services/document_checklist/** сопровождается diff в features/document-checklist.yaml. Если нет - предупреждение (не блокировка, чтобы не мешать в крайних случаях).

Альтернатива - агентский pre-commit: agent читает diff, читает текущий узел, проверяет, нужно ли обновить, и если да - предлагает diff в YAML.

4. Один owner + структурированные contributors

Без явной роли owner документ умирает. У уровня Operation/Job - один owner на всю иерархию (обычно из research-команды). У Feature Registry - один owner на узел или на группу узлов (часто team owner фичи).

Contributors структурированы по типу контента:

5. Quarterly review

Раз в квартал owner проходит по всему документу и делает sanity check. Это не replacement для непрерывного обновления, а защита от того, что некоторые узлы перестали обновляться, а никто не заметил.

Production-evidence

Антипаттерны

  1. «У нас есть DDD, этого хватит». Bounded context структурирует код, но не functional surface. Один auth-context = регистрация + login + reset + MFA + SSO + session management. Это разные фичи с разным lifecycle, разными pain points, разными owners. DDD - необходимое, не достаточное. Подробнее про DDD - в статье о Domain-Driven Design.
  2. «У нас всё в Jira / Linear». Тикеты не имеют schema, не привязаны к коду через стабильные refs (links - это weak refs), фрагментированы по эпикам, чьи границы кто как нарисовал. MCP-мост помогает агенту читать тикеты, но не превращает тикеты в feature inventory.
  3. Happy-path-only документация. Без pain_points, constraints, unhappy_paths AI генерирует generic-гипотезы. «Давайте автоматизируем шаг 3» - типичный output, когда AI не знает, что 40% клиентов на шаге 3 промахиваются. Слой проблем превращает документ из «как должно быть» в «как реально».
  4. «Задокументируем сразу всё». 50 jobs с happy-path-only хуже, чем 10 jobs с полными полями. Глубина выигрывает у ширины: agent работает с ограниченным числом высококачественных узлов лучше, чем с большим числом средне-качественных.
  5. Свободный текст там, где должна быть структура. Agents парсят tables, code blocks, structured fields лучше, чем prose. Если поле имеет фиксированный набор значений (lifecycle: active|deprecated|behind_flag|migrating) - это enum, не свободный текст.
  6. Tool lock-in. «Всё в Backstage», «всё в Kiro», «всё в Notion». Один инструмент - одна точка отказа. Структурированный YAML рядом с кодом + автоматический mirror в developer portal - устойчивее, чем монолит на одном вендоре.
  7. Дублирование того, что живёт в других системах. Узел Feature не содержит копию description из Jira. Он содержит reference. Иначе - два источника истины, которые разойдутся.
  8. agent_hints как догадка автора. Поля типа files_likely_affected, failure_modes, concurrency_risks ценны, когда они построены на реальной статистике (commits, incidents, postmortems). Если автор додумал их «на глаз», агент работает на догадках, и доверие к узлам падает.
  9. Документ-снимок вместо живого документа. Без update-триггеров, owner и review feature catalog умрёт. Хуже, чем его отсутствие: агент будет генерировать ответы по устаревшим данным с уверенным тоном.

Минимальная рабочая система

Чтобы не утонуть в попытке покрыть всё сразу, начинать стоит с одного critical journey и 5-7 фич.

Шаг 1: выбрать одну critical operation

P0 для бизнеса, частая (high volume), с понятным actor и outcome. Примеры: онбординг нового пользователя, основной checkout flow, intake critical-path. Не выбирать low-traffic admin-флоу - там пробел не виден агенту, потому что задачи редко приходят в этом периметре.

Шаг 2: разметить 1 Operation + 2-3 Jobs

YAML-файл на каждую сущность. Поля минимум: id, name, description, actor, cadence, features (refs). Для Jobs: jtbd, journey (happy path с actor-метками), features. Pain points / constraints / unhappy paths - в следующей итерации.

Шаг 3: Feature Registry с 5-7 узлами

Узлы для фич, упомянутых в jobs. Поля минимум: id, name, purpose, lifecycle, files_likely_affected, acceptance_criteria (хотя бы 1-2). Остальные поля (contracts, invariants, failure_modes) - итеративно по мере того, как реально проявляются.

Шаг 4: schema-валидация в CI

JSON Schema на формат + один CI-job, который запрещает мерджить YAML, не проходящий валидацию. Это превращает узлы из «wiki» в проверяемый артефакт.

Шаг 5: один pilot ADR-as-rule

Один ADR с companion executable rule (стиль archgate / AgDR). Например: «Все uploads в hot-path онбординга должны иметь timeout < 5s» - превращается в lint-rule, который агент проверяет автоматически.

Шаг 6: один drift-job

Cron-job раз в неделю: для каждого узла Feature проверить, что files_likely_affected содержат коммиты за последние 90 дней, и что flag_refs существуют в реестре флагов. Создаёт issues для owner, не блокирует.

Шаг 7: подключить MCP-мосты

К Jira/Linear (актуальные in-flight тикеты по jobs/features), к GitHub (PRs и issues по features), к реестру флагов (current state). Это нулевая стоимость авторинга, поэтому первый шаг.

Через месяц - решение продолжать или сворачивать

Метрики, по которым решать (грубо):

Если месяц прошёл, и эти метрики растут - расширять (следующая operation, больше узлов). Если документ обновляется реже коммитов в фичах, которые он описывает, - сворачивать или менять модель (возможно, проблема в process, а не в формате).

Что не делать на старте:

Источники

Теория и practice

Industry artefacts and tools

Adjacent references

Смежные статьи на сайте