8 lines
19 KiB
JSON
8 lines
19 KiB
JSON
{
|
||
"index": 92,
|
||
"slug": "editorial-2025-06-mechanism-developer-experience",
|
||
"title": "DX внутреннего инструмента: как доказать, где ломается путь задачи",
|
||
"excerpt": "Время ожидания не объясняет удобство внутреннего инструмента. Разбираем контракт одной задачи: события, роль, наблюдение, сигнал поддержки, отрицательный путь и критерий, который не позволяет объявить гипотезу улучшением без сравнимых данных.",
|
||
"contentHtml": "<p>Заявка во внутреннем инструменте может завершиться успешно, а разработчик всё равно не поймёт, кто отвечает за следующий шаг. Он ищет владельца в чате, повторяет уже введённые данные и держит задачу открытой до непонятного результата. Ошибка редко видна в статусе: система показывает <code>approved</code>, но не показывает, почему путь занял время и что делать при тишине.</p><p>Цена такой ошибки — не только минуты. Теряется контекст, растёт поток уточнений, support повторяет одну и ту же инструкцию, а команда может начать переделку по единичному громкому отзыву. Если измерить только время от submit до результата, эти причины смешаются.</p><p><strong>Тезис.</strong> Удобство внутреннего инструмента нужно проверять на границе одной задачи. Контракт должен отделять факт перехода от того, как его понял человек, от сигнала поддержки, решения владельца и доказательства эффекта. Пока сопоставимого сравнения нет, вывод остаётся <code>not-established</code>.</p><h2>Механизм: задача вместо общего DX-score</h2><p>Задача — это не экран и не весь сервис. Это путь одной объявленной роли от ясного входа к проверяемому результату. Например, роль инженера открывает запрос на доступ к sandbox. Вход можно сформулировать так: «запрос отправлен с указанным окружением». Результат — «доступ подтверждён и его можно проверить». Между ними видны этапы: открытие, отправка, начало ожидания approval, получение approval и подтверждение результата.</p><p>У каждого этапа должны быть имя, порядок, время факта и источник записи. Время факта отвечает на вопрос «когда переход произошёл». Время наблюдения отвечает на другой вопрос: «когда источник его зафиксировал». Если эти значения совпадают в учебном примере, это не обещает такой же доставки в реальной системе.</p><p>OpenTelemetry разделяет traces, metrics и logs как разные сигналы. В его семантических соглашениях событие несёт timestamp момента, когда оно произошло. Это полезная дисциплина для контракта: событие и измерение нельзя заменять свободным текстом. Но стандарт не выбирает за команду UX-метрику и не доказывает причину задержки.</p><h2>Поля, которые удерживают смысл</h2><table><thead><tr><th>Поле</th><th>Зачем нужно</th><th>Проверка</th><th>Что нельзя выводить</th></tr></thead><tbody><tr><td><code>declaredRole</code></td><td>Описывает, для кого рассматриваем путь.</td><td>Роль совпадает с объявленной записью.</td><td>Она не равна реальному пользователю или его правам.</td></tr><tr><td><code>stage</code> и порядок</td><td>Показывают, где находится переход.</td><td>Список этапов фиксирован и упорядочен.</td><td>Порядок не объясняет причину ожидания.</td></tr><tr><td><code>occurredAt</code> и <code>observedAt</code></td><td>Разделяют время факта и время фиксации.</td><td>ISO-время, <code>occurredAt ≤ observedAt</code>, хронология.</td><td>Время не измеряет cognitive cost.</td></tr><tr><td><code>waitBucket</code></td><td>Даёт диапазон без ложной точности.</td><td>Корзина разрешена только для нужного этапа.</td><td>Корзина не является оценкой DX.</td></tr><tr><td><code>evidenceKind</code> и <code>source</code></td><td>Не дают наблюдению притвориться событием.</td><td>Для каждого вида задана допустимая пара.</td><td>Источник сам по себе не делает тезис причинным.</td></tr><tr><td><code>knownUnknowns</code></td><td>Сохраняют пробелы рядом с решением.</td><td>Есть непустой список конкретных неизвестных.</td><td>Неизвестное нельзя заменить удобной догадкой.</td></tr></tbody></table><p>Строгий контракт нужен не ради красивого JSON. Он задаёт место отказа. Лишнее поле вроде <code>unboundedScore</code> меняет смысл записи и должно быть отвергнуто так же, как пропущенное обязательное поле. Разреженный массив, неверная версия модели или неизвестный источник должны закрывать проверку. Иначе один слой назовёт запись событием, другой — наблюдением, а третий построит на ней решение.</p><h2>Пять разных операций свидетельства</h2><p><strong>Instrumented event</strong> фиксирует переход: запрос отправлен или approval получен. <strong>UX-observation</strong> описывает понимание шага: роль не видит ответственного после отправки. <strong>Support signal</strong> группирует формулировку вопроса, например <code>routing-unclear</code>. <strong>Candidate change</strong> задаёт ограниченную гипотезу: показать owner до submit. <strong>Effect evidence</strong> появляется только после повторной проверки по той же границе.</p><p>Эти объекты нельзя переставить местами. Событие не говорит, что ожидание плохо. Наблюдение не доказывает, что так происходит у всех. Сигнал поддержки не является счётчиком обращений и не устанавливает причину. Гипотеза не равна результату. Если система не сохранила сопоставимое сравнение, безопасный ответ — остановиться, а не дописать эффект в отчёт.</p><figure><img src=\"/assets/editorial/2025/developer-experience-2025-feedback-loop.svg\" alt=\"Петля Observe, Decide, Change, Recheck и Effect evidence с безопасной остановкой без сравнимых данных\"><figcaption>Контракт ведёт от наблюдения к ограниченному изменению. Без сравнимого evidence цикл заканчивается safe stop. Схема учебная.</figcaption></figure><h2>Учебный пример с отрицательным путём</h2><p>Ниже приведён только учебный in-memory пример. Он не обращается к внутреннему инструменту, не содержит пользователей, заявок, telemetry или production-результатов. Пусть фиксированная запись описывает один sandbox-запрос. Между отправкой и получением approval стоит корзина <code>30m–1h</code>. Роль не знает владельца после submit. Это три разных факта: этап, наблюдение и вопрос маршрутизации.</p><pre><code>const input = createFixedJourneyInput(); input.claimedEffect.status = 'established'; input.claimedEffect.evidenceRefs = ['one-record-is-not-a-comparison']; const result = evaluateJourney(input); console.log(result.reason); // effect-claim-not-evidenced; console.log(result.effectClaim); // not-established</code></pre><p>Проверка должна отвергнуть такой input. Одна запись показывает, что модель умеет представить путь. Она не показывает частоту, стоимость ожидания, причину ручного обхода или улучшение после изменения. Поле <code>effectState</code> остаётся <code>false</code>. Даже принятый decision означает только «гипотезу можно проверить в ограниченном follow-up», а не «изменение разрешено к выпуску».</p><p>Отрицательный путь важнее happy path. Если валидатор принимает голословный <code>established</code>, команда быстро перенесёт вывод на другие роли и задачи. Если сравнение меняет роль, ожидаемый результат, порядок этапов или источник данных, разница может появиться из-за другой границы, а не из-за изменения интерфейса.</p><h2>Симптом → причина → проверка → действие</h2><table><thead><tr><th>Симптом</th><th>Вероятная причина</th><th>Проверка</th><th>Действие</th></tr></thead><tbody><tr><td>Результат успешен, но человек спрашивает «к кому идти».</td><td>Owner не виден на этапе маршрутизации.</td><td>Сопоставить UX-observation с конкретным stage.</td><td>Проверить показ owner до submit; не обещать сокращение approval.</td></tr><tr><td>В dashboard растёт время до результата.</td><td>В одну метрику попали очередь, доставка события и ручная работа.</td><td>Разделить stage events, <code>occurredAt</code> и <code>observedAt</code>.</td><td>Проверить источник и задержку доставки отдельно.</td></tr><tr><td>Есть один громкий тикет про неудобство.</td><td>Сигнал поддержки приняли за распространённый эффект.</td><td>Проверить категорию сигнала и неизвестный denominator.</td><td>Назначить вопрос и owner; не строить общий DX-score.</td></tr><tr><td>После изменения «стало лучше».</td><td>Сравнили разные роли или разные задачи.</td><td>Сверить comparison boundary и порядок этапов.</td><td>Вернуть claim в <code>not-established</code> и повторить сопоставимый проход.</td></tr><tr><td>Валидатор принимает запись с лишним полем.</td><td>Контракт проверяет наличие, но не точный набор ключей.</td><td>Запустить exact-key и canonical-JSON проверки.</td><td>Отклонять лишние и пропущенные поля до решения.</td></tr></tbody></table><h2>Почему wait bucket не равен cognitive cost</h2><p>Когнитивная цена возникает между видимыми событиями. Человек ищет инструкцию в другом чате, сравнивает похожие формы, сомневается, повторно отправляет запрос или запоминает обходной путь. Можно ждать недолго и всё равно потратить много внимания. Можно ждать долго из-за внешнего окна и не считать это дефектом интерфейса.</p><p>Поэтому корзина времени только указывает участок для исследования. Она не объясняет причину и не превращается в score. Нельзя умножить <code>30m–1h</code> на observation «owner неясен» и получить измерение удобства. Это разные данные, у которых разные владельцы и разные способы проверки.</p><figure><img src=\"/assets/editorial/2025/developer-experience-2025-wait-time-histogram.svg\" alt=\"Учебная гистограмма корзин ожидания 0–5 минут, 30 минут–1 час и более часа\"><figcaption>Корзина времени помогает выбрать участок пути. Числа учебные и не описывают реальный инструмент.</figcaption></figure><h2>Порядок действий</h2><ol><li>Назовите одну задачу, одну declared role и проверяемый результат. Не включайте весь onboarding в один маршрут.</li><li>Опишите этапы, допустимый порядок, источник каждого события и два времени: факт и наблюдение.</li><li>Добавьте wait bucket только как диапазон и отдельно запишите, чего он не объясняет.</li><li>Сформулируйте UX-вопрос и support signal. Не превращайте ни один из них в готовую причину.</li><li>Назначьте owner, одну candidate change, comparison boundary и bounded follow-up.</li><li>Заранее задайте stop condition: если граница или источник не сопоставимы, claim остаётся <code>not-established</code>.</li><li>После повторной проверки сравните ту же роль, задачу, порядок этапов и виды evidence. Только затем решайте, есть ли основание для нового claim.</li></ol><h2>Ограничения и критерий готовности</h2><p>Контракт проверяет структуру и границы данных, но не правдивость внешнего мира. Он не заменяет user research, проверку безопасности telemetry, согласие на сбор данных, анализ support-категорий или измерение реальной выборки. Он также не объясняет причинность: одинаковый результат до и после изменения может быть следствием другой нагрузки, инструкции или внешнего процесса.</p><p>Учебный код не содержит transport, client, user identifier, retention policy или integration point. Его положительный результат означает только согласованность фиксированных литералов. Его отрицательный результат не доказывает, что реальный инструмент неудобен или что предложенное изменение поможет.</p><p><strong>Проверяемый критерий готовности:</strong> для одной разрешённой задачи существуют две записи с одинаковыми role, objective, stage order, evidence labels и comparison boundary; каждая запись проходит exact-key и timestamp-проверки; изменение и bounded window задокументированы; а effect claim либо опирается на это сопоставление, либо явно остаётся <code>not-established</code>. Если хотя бы одно условие не выполнено, работа готова только к следующему исследовательскому шагу, но не к заявлению об улучшении.</p><h2>Проверяемые источники</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: Signals</a> — официальное описание различий между traces, metrics и logs.</li><li><a href=\"https://opentelemetry.io/docs/specs/semconv/general/events/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: Semantic conventions for events</a> — официальные требования к timestamp события и его моделированию.</li><li><a href=\"https://www.gov.uk/service-manual/measuring-success/usability-benchmarking-a-website-or-whole-service\" target=\"_blank\" rel=\"noopener\">GOV.UK Service Manual: usability benchmarking</a> — официальное руководство о сочетании performance metrics и user research.</li></ul>"
|
||
}
|