DarkRiDDeR13 мин

Удобство внутреннего инструмента: контракт наблюдаемой задачи

DXКоманда

Проблема. Инструмент может успешно завершать запрос, но инженер всё равно ждёт неизвестного владельца, повторяет данные вручную и открывает обходной путь. Цена ошибки — очередь из невидимых переключений контекста: владелец видит только успешные ответы, а команда платит за уточнения, повторные заявки и решения, которые нельзя проверить.

Техническая реакция часто слишком узка: добавить один таймер, построить среднее и назвать его 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 не заменяет разговор о смысле задачи.

Вертикальная петля Observe, Decide, Change, Recheck и Effect evidence, в которой UX-observation, event и support signal разделены, а без сопоставимого evidence выбран safe stop.
Рисунок 3. Решение появляется после наблюдений, а доказательство эффекта — только после повторной проверки по той же границе.

Руководство 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, а effectfalse.

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 может быть другая классификация, а событие может не покрывать ручной шаг. Поэтому отрицательный результат выглядит скромно: «в этой границе доказательства нет». Для техлида это полезный ответ — он сохраняет место для следующего измерения и не тратит квартал на спор о метрике, которой никто не может объяснить.

Учебная гистограмма ожидания с тремя временными корзинами и подписью, что корзина показывает место исследования, но не объясняет причину или удобство.
Рисунок 4. Время помогает выбрать участок пути для проверки. Оно не заменяет evidence о понимании, поддержке или эффекте.

Как применить контракт без ложной универсальности

Переносить следует не значения из fixture, а порядок работы. Реальная команда отдельно определяет права на сбор данных, защиту идентификаторов, допустимые sources, retention и owner. Сначала ей нужен task boundary, потом event naming, затем разрешённый user research и support categorisation. После изменения она повторяет ту же задачу для сопоставимой роли и заранее знает, что сделает, если evidence недостаточно.

  1. Сформулируйте цель одной задачи и ожидаемый результат в наблюдаемой форме.
  2. Назовите role, stages, event source и wait bucket; уберите поля, которые никто не сможет интерпретировать.
  3. Отдельно запишите UX question, support category и known unknowns.
  4. Назначьте owner candidate change и запретите эффект claim до bounded follow-up.
  5. Повторите сравнение на той же границе или верните решение в 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 указывает направление проверки; он не заменяет исследование, не показывает причинность и не является готовым решением.