Проблема. Инструмент может успешно завершать запрос, но инженер всё равно ждёт неизвестного владельца, повторяет данные вручную и открывает обходной путь. Цена ошибки — очередь из невидимых переключений контекста: владелец видит только успешные ответы, а команда платит за уточнения, повторные заявки и решения, которые нельзя проверить.
Техническая реакция часто слишком узка: добавить один таймер, построить среднее и назвать его DX. Это стирает разницу между событием, наблюдением, сигналом поддержки, решением и доказательством эффекта. Нужен не широкий dashboard, а строгий контракт одной задачи. Он делает неизвестное видимым, не позволяет подменить role или результат и заставляет остановить claim, когда сопоставимых данных нет.
Единица наблюдения — задача с границей
Задача — не экран и не сервис целиком. Это переход от ясного входа к проверяемому результату для одной declared role. В fixed model входом служит открытие synthetic access request, результатом — synthetic confirmation. Между ними есть пять stage events. Каждый event содержит два времени: occurredAt — когда факт задан в модели, и observedAt — когда он зафиксирован. Их равенство в fixture не обещает, что так будет в настоящем инструменте; это просто делает допущение явным.
OpenTelemetry Trace API полезен здесь только как пример дисциплины данных: span имеет start/end timestamps и набор timestamped events, а event имеет name, timestamp и optional attributes. В model мы не строим trace и не отправляем telemetry. Мы заимствуем ровно одно свойство: без имени события, времени и источника невозможно проверить, что сравнивают. W3C Performance Timeline таким же образом показывает, почему у записи должны быть name, type, startTime и duration, но не подсказывает, какую бизнес-метрику считать.
Поля контракта и их неочевидные границы
| Поле | Зачем нужно | Проверка | Запрещённый вывод |
|---|---|---|---|
| declaredRole | Отделяет путь одной роли от общего «пользователя». | Exact match с fixed role record. | Роль не равна реальной учётной записи или праву. |
| stage + order | Делает видимым переход и место задержки. | Пять допустимых стадий в строгом порядке. | Порядок не объясняет причину ожидания. |
| occurredAt / observedAt | Разделяет время факта и время его фиксации. | ISO-формат, occurredAt ≤ observedAt, хронология. | Время не измеряет cognitive cost. |
| waitBucket | Помечает диапазон ожидания, не создавая ложную точность. | Для approval stages только 30m–1h. | Корзина не является общим DX-score. |
| evidenceKind / source | Не даёт observation выдать за event или signal за доказательство. | Фиксированная пара kind → source. | Источник не делает утверждение причинным. |
| knownUnknowns | Сохраняет пробелы модели рядом с решением. | Непустой dense array обязательных строк. | Unknown нельзя заменить удобным предположением. |
Exact contract — это не бюрократия ради JSON. Он задаёт точку, в которой evaluator обязан отказать. Лишнее поле unboundedScore отвергается так же, как недостающее knownUnknowns. Удалённый элемент array становится sparse и тоже отвергается. Циклический объект нельзя канонизировать — значит его нельзя использовать как незаметный носитель контекста. Эти проверки защищают не от злого пользователя, а от тихого расползания смыслов между заметкой, таблицей и решением.
Пять слов «свидетельства» означают пять разных операций
UX-observation — наблюдение о том, как declared role понимает шаг; в примере это неясный owner после submit. Instrumented event — контрактный факт о переходе. Support signal — группировка формулировки вопроса, которой может воспользоваться команда. Decision — выбор owner: показать owner до submit и проверить именно этот этап. Effect evidence — сопоставимый результат после изменения. Нельзя переставить их местами: support signal не устанавливает эффект, а event не заменяет разговор о смысле задачи.
Руководство GOV.UK советует не опираться только на digital analytics, а сочетать метрики с user research. Для support оно отдельно предлагает использовать feedback в улучшении и связывать сигналы с внутренней группой, которая способна действовать. В этой статье из этого следует узкий инженерный вывод: signal должен иметь owner и следующий вопрос, но не получает статус универсальной метрики или готового backlog item.
Fail-closed evaluator: отказ лучше выдуманного эффекта
Evaluator сначала проверяет, что весь input — canonical JSON из plain records и dense arrays. Затем он сверяет exact keys, model version, declared role, event source, порядок и wait bucket. После этого он проверяет change, bounded follow-up и effect claim. В нормальном case decision разрешает только candidate change hypothesis; поле effectClaim остаётся not-established, а effect — false.
import { createFixedJourneyInput, evaluateJourney } from './upgrade-2025-06.mjs';
const forged = createFixedJourneyInput();
forged.claimedEffect.status = 'established';
forged.claimedEffect.evidenceRefs = ['one-record-is-not-a-comparison'];
const result = evaluateJourney(forged);
console.log(result.reason);
// effect-claim-not-evidenced
console.log(result.nextAction);
// Safe stop: preserve the rejected claim ...
Этот отрицательный путь важнее happy path. Если одна fixed record уже названа эффектом, следующая команда почти неизбежно перенесёт вывод на другие роли и задачи. Без comparison boundary она не заметит, что изменились инструкция, входная роль, порядок этапов или канал обращения. Поэтому claim сначала должен быть слабым и точно сформулированным: «effect not established».
Почему wait bucket не равен когнитивной стоимости
Cognitive cost проявляется не только во времени. Человек может ждать недолго и всё же переключаться между системами, искать ответ, сомневаться в результате или выполнять ручный обход. И наоборот, долгий этап может быть ожидаемым внешним ограничением. В model wait bucket помечает approval wait, а UX-observation фиксирует непонятного owner. Эти объекты лежат рядом, но evaluator не умножает их и не строит synthetic score.
Разделение occurredAt и observedAt защищает ещё от одной подмены. Если источник заметил событие позже самого перехода, это может быть свойством доставки записи, а не свойством задачи. Fixture задаёт одинаковые значения, чтобы не моделировать транспорт, но evaluator всё равно требует явный порядок. В production-подобной системе надо отдельно решить, какой timestamp отвечает на вопрос о пути, а какой — на вопрос о наблюдателе; текст не переносит это решение автоматически.
Не следует также превращать отсутствие сигнала в доказательство удобства. У роли может не быть доступного канала, у support может быть другая классификация, а событие может не покрывать ручной шаг. Поэтому отрицательный результат выглядит скромно: «в этой границе доказательства нет». Для техлида это полезный ответ — он сохраняет место для следующего измерения и не тратит квартал на спор о метрике, которой никто не может объяснить.
Как применить контракт без ложной универсальности
Переносить следует не значения из fixture, а порядок работы. Реальная команда отдельно определяет права на сбор данных, защиту идентификаторов, допустимые sources, retention и owner. Сначала ей нужен task boundary, потом event naming, затем разрешённый user research и support categorisation. После изменения она повторяет ту же задачу для сопоставимой роли и заранее знает, что сделает, если evidence недостаточно.
- Сформулируйте цель одной задачи и ожидаемый результат в наблюдаемой форме.
- Назовите role, stages, event source и wait bucket; уберите поля, которые никто не сможет интерпретировать.
- Отдельно запишите UX question, support category и known unknowns.
- Назначьте owner candidate change и запретите эффект claim до bounded follow-up.
- Повторите сравнение на той же границе или верните решение в safe stop.
Ограничения и следующий тест
Граница механизма. Evaluator проверяет структуру и семантические пары fixed records, а не правдивость внешнего мира. Он намеренно не содержит payload, user identifier, transport, часы или integration point. Даже accepted decision не запускает change: он только разрешает сохранить hypothesis для ограниченной следующей проверки.
Даже строгая fixture не моделирует реальную задержку, человека, поддерживающую команду или качество интерфейса. Она не знает безопасность telemetry, не проверяет доступы и не подменяет user research. Её результат — только доказательство того, что неверный contract не станет положительным решением.
Следующий проверяемый шаг. Возьмите один реальный, но разрешённый для исследования путь и составьте сначала пустой contract: какие fields вы обязуетесь собрать, какие unknowns ожидаете и какой result заставит остановить claim. Проверьте этот contract с owner до изменения интерфейса.
Проверяемые источники
- OpenTelemetry Trace API (v1.31.0, immutable commit 3985e212f0ef5439daf9b10f0ee490e349c69368, 13 March 2024). Версия v1.31.0 описывает span как операцию со start/end timestamps, attributes и timestamped events; event содержит name, timestamp и optional attributes. Граница: Это контракт observability API. Он не определяет UX, не назначает owner и не доказывает, что конкретная команда правильно измеряет путь.
- W3C Performance Timeline (Candidate Recommendation Draft, 21 May 2025). Рекомендация определяет PerformanceEntry и поля name, entryType, startTime и duration для наблюдения временных записей. Граница: Стандарт относится к web performance API; здесь он служит только примером явного имени, типа и времени записи, не рецептом измерения внутреннего сервиса.
- GOV.UK Service Manual: Measuring the success of your service (immutable Internet Archive capture 2024-07-05T18:32:52Z of official GOV.UK guidance; public update 6 August 2018). Закреплённый снимок предлагает сочетать performance metrics с user research, не полагаться только на digital analytics и для пути смотреть completion/time периодически. Граница: Это публичная методическая рекомендация, а не универсальный KPI, договор об измерениях или доказательство эффекта этой учебной модели.
- GOV.UK Service Manual: Set up and manage user support (immutable Internet Archive capture 2023-05-12T13:53:36Z of official GOV.UK guidance; public update 24 November 2016). Закреплённый снимок предлагает использовать feedback user support для улучшения, группировать запросы и связывать их с внутренней командой, способной действовать. Граница: Support signal указывает направление проверки; он не заменяет исследование, не показывает причинность и не является готовым решением.