Files
progcode/editorial/agent-rewrites/093.json
T
huncode 2d914b543f
Build and deploy / deploy (push) Failing after 15s
Publish rewritten technical article archive
2026-08-02 22:19:34 +03:00

8 lines
20 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"index": 93,
"slug": "editorial-2025-06-practice-developer-experience",
"title": "DX внутреннего инструмента: найти место, где застревает задача",
"excerpt": "Как разобрать путь одной задачи, отделить событие от причины ожидания и принять решение только при сопоставимой проверке.",
"contentHtml": "<p>Внутренний инструмент отвечает <code>200 OK</code>, но задача не заканчивается. Разработчик отправляет заявку, не видит следующего владельца, открывает чат и повторяет вопрос. В журнале есть успешный запрос. В интерфейсе нет ошибки. Симптом появляется между двумя этапами: система приняла действие, а человек не понял, что делать дальше.</p>\n<p>Цена ошибки выше времени одного ожидания. Разработчик переключается между системами и повторяет ввод. Поддержка отвечает на одинаковые вопросы. Владелец инструмента видит хорошие технические статусы и откладывает проблему. Затем команда чинит заметный экран, хотя задержку создаёт очередь согласования или неясное право доступа.</p>\n<p><strong>Тезис.</strong> Developer experience нельзя надёжно оценить одним средним временем или общим баллом. Нужно взять одну задачу, провести её от входа до результата и сохранить границы каждого вывода. Событие показывает переход. Наблюдение описывает действие человека. Сигнал поддержки указывает вопрос. Решение выбирает изменение. Эффект подтверждает только сопоставимое повторение.</p>\n<h2>Единица разбора — одна задача</h2>\n<p>Не начинайте с всего onboarding и не смешивайте разные роли. Возьмите один повторяемый путь. Например, инженер запрашивает доступ к sandbox. Ожидаемый результат — подтверждение или объяснимый отказ. Между ними находятся открытие заявки, отправка, начало ожидания, решение владельца и подтверждение результата.</p>\n<p>У задачи должны быть четыре явные границы. Первая — declared role: кто выполняет действие. Вторая — objective: зачем он его выполняет. Третья — expected result: что можно увидеть и проверить. Четвёртая — comparison boundary: какие условия обязаны совпасть при повторной проверке. Без этих полей сравнение легко превращается в сравнение разных задач.</p>\n<p>Временная метка помогает найти участок пути. Она не объясняет причину. <code>occurredAt</code> может обозначать момент перехода, а <code>observedAt</code> — момент его фиксации. Если запись пришла позже, это свойство наблюдения, а не обязательно задержка процесса. Поэтому время нужно хранить рядом с источником и этапом, а не превращать в самостоятельную оценку удобства.</p>\n<pre><code>const journey = {\n role: 'platform-engineer',\n objective: 'получить sandbox access',\n expectedResult: 'confirmation или объяснимый отказ',\n stages: [\n 'task.opened',\n 'request.submitted',\n 'approval.wait.started',\n 'approval.received',\n 'result.confirmed'\n ],\n waitBucket: '30m-1h',\n nextOwner: 'unknown',\n uxObservation: 'после submit неясен следующий шаг',\n supportSignal: 'routing-unclear',\n effectClaim: 'not-established'\n};</code></pre>\n<p>Код — учебный пример в памяти. Он не обращается к сети, не отправляет telemetry и не описывает реальную заявку. Его задача — показать минимальный набор полей и место, где система должна остановиться. В рабочем инструменте отдельно определяют разрешённые данные, права доступа, срок хранения и правила удаления идентификаторов.</p>\n<h2>Механизм: пять свидетельств отвечают на разные вопросы</h2>\n<p>Событие отвечает: «Что произошло и когда?» Например, заявка перешла из <code>submitted</code> в <code>approval.wait.started</code>. Оно не отвечает, почему человек открыл чат.</p>\n<p>UX-наблюдение отвечает: «Как человек понял или не понял шаг?» Запись «после отправки человек ищет владельца в другом канале» полезнее диагноза «плохой интерфейс». Наблюдение ещё не говорит, что добавление label исправит путь.</p>\n<p>Support signal отвечает: «Какой вопрос повторяется и какая команда может его проверить?» Категория <code>routing-unclear</code> помогает сгруппировать обращения. Она не показывает долю всех пользователей и не доказывает причинность.</p>\n<p>Решение отвечает: «Какое ограниченное изменение проверяем, на каком этапе и кто отвечает?» Хорошее решение содержит owner, target stage, candidate change, окно проверки и условие остановки.</p>\n<p>Effect evidence отвечает: «Что изменилось при той же границе?» Для него нужны та же роль, тот же результат, сопоставимый порядок этапов и заранее определённый признак. Одна удачная запись не становится evidence только потому, что её удобно показать.</p>\n<h2>Симптом → причина → проверка → действие</h2>\n<div class=\"table-scroll\"><table><caption>Диагностика пути внутреннего инструмента</caption><thead><tr><th scope=\"col\">Симптом</th><th scope=\"col\">Причина</th><th scope=\"col\">Проверка</th><th scope=\"col\">Действие</th></tr></thead><tbody><tr><td>Заявка успешна, но человек повторяет вопрос</td><td>Следующий владелец или шаг не виден</td><td>Восстановить путь от submit до следующего действия одной роли</td><td>Записать UX-наблюдение и проверить видимость owner</td></tr><tr><td>Среднее время ожидания растёт</td><td>В одну метрику попали разные роли и этапы</td><td>Разделить stage, role и wait bucket</td><td>Выбрать одну границу задачи и не строить общий DX-score</td></tr><tr><td>Один отзыв сразу превращается в правку</td><td>Наблюдение смешали с решением</td><td>Отделить действие, вопрос, гипотезу и неизвестное</td><td>Сформулировать candidate change с owner</td></tr><tr><td>Категорию поддержки называют доказательством эффекта</td><td>Нет сопоставимого результата после изменения</td><td>Проверить source, роль, период и тот же ожидаемый результат</td><td>Оставить claim как <code>not-established</code></td></tr><tr><td>После изменения стало «удобнее»</td><td>Повторили другой маршрут или изменили состав роли</td><td>Сравнить objective, stage order, fields и окно наблюдения</td><td>Остановить вывод и повторить задачу по прежней границе</td></tr></tbody></table></div>\n<p>Таблица не заменяет разговор с человеком и не создаёт статистику из одной записи. Она заставляет назвать следующий проверяемый шаг. Если действие нельзя выполнить или его результат нельзя увидеть, это не действие, а пожелание.</p>\n<h2>Почему ожидание не равно причине</h2>\n<p>Корзина <code>30m-1h</code> говорит только о диапазоне между двумя событиями. В одном случае человек не понимает, что заявка уже стоит в очереди. Тогда нужно проверить статус, владельца и текст подтверждения. В другом случае владелец понятен, но согласование зависит от внешнего окна. Тогда интерфейс может честно объяснить ограничение, но не сократить сам процесс.</p>\n<p>Одинаковый wait bucket поэтому ведёт к разным решениям. Нельзя строить DX-score из времени и выдавать его за причину. Нельзя считать отсутствие обращения доказательством удобства: человек мог не иметь канала поддержки, а событие могло не видеть ручной шаг в соседней системе.</p>\n<figure><img src=\"/assets/editorial/2025/developer-experience-2025-task-journey.svg\" alt=\"Схема пути одной задачи: вход, отправка, ожидание, сигнал поддержки, решение владельца, повторная проверка и остановка без доказательства эффекта\" loading=\"lazy\" /><figcaption>Учебная иллюстрация. Красная ветка означает остановку вывода, если повторная проверка не сопоставима с исходной задачей.</figcaption></figure>\n<p>Источник каждого факта должен быть виден рядом с ним. Запись журнала подтверждает событие. Интервью или наблюдение подтверждает вопрос человека. Категория поддержки подтверждает повторяемую формулировку. Ни один источник не заменяет остальные. OpenTelemetry полезен здесь как пример дисциплины временных событий: имя и время помогают восстановить переход, но не отвечают на продуктовый вопрос о понятности шага.</p>\n<h2>Отрицательный путь: остановиться при слабом доказательстве</h2>\n<p>Представим, что после одной записи команда меняет <code>effectClaim</code> на <code>established</code>. В качестве evidence она прикладывает ту же запись. Такой вывод нужно отклонить. Нет второй точки сравнения. Неизвестно, совпала ли роль. Нельзя отделить эффект изменения от внешнего окна согласования.</p>\n<pre><code>function decide(claim) {\n const comparable = claim.sameRole &&\n claim.sameObjective &&\n claim.sameStageBoundary &&\n claim.evidenceCount &gt;= 2;\n\n if (claim.status === 'established' &amp;&amp; !comparable) {\n return {\n status: 'HOLD',\n reason: 'effect-claim-not-evidenced'\n };\n }\n\n return { status: 'needs-owner-decision' };\n}</code></pre>\n<p>Это тоже учебный пример. Функция проверяет условие остановки на объекте в памяти. Она не оценивает правдивость внешних данных и ничего не меняет в production. В настоящем валидаторе понадобятся проверки схемы, порядка этапов, формата времени, источника события и разрешений.</p>\n<p><code>HOLD</code> не означает, что гипотеза неверна. Он означает, что текущая запись не отвечает на вопрос. Это защищает команду от преждевременного переноса решения на другие роли и задачи. Если показ owner уменьшит число вопросов, повторная проверка это обнаружит. Если причина лежит во внешней очереди, результат будет другим: интерфейс улучшит объяснение, но не сократит ожидание.</p>\n<h2>Порядок действий</h2>\n<ol><li><strong>Назовите одну задачу.</strong> Зафиксируйте role, objective и ожидаемый результат. Не включайте весь onboarding в один маршрут.</li><li><strong>Опишите этапы.</strong> Задайте порядок событий, source, <code>occurredAt</code>, <code>observedAt</code> и допустимые wait buckets.</li><li><strong>Соберите наблюдения.</strong> Запишите видимое действие или вопрос человека без диагноза. Отдельно сохраните support signal и known unknowns.</li><li><strong>Выберите одну гипотезу.</strong> Назначьте owner, target stage, candidate change и признак, который можно проверить.</li><li><strong>Зафиксируйте границу сравнения.</strong> Сохраните ту же роль, цель, ожидаемый результат и порядок этапов. Заранее задайте окно повторной проверки.</li><li><strong>Проверьте отрицательные входы.</strong> Подайте неизвестного owner, пропущенный source, нарушенный порядок и неподтверждённый effect claim. Для каждого ожидайте остановку с причиной.</li><li><strong>Примите ограниченное решение.</strong> Передавайте изменение дальше только при сопоставимом evidence. Иначе сохраните <code>not-established</code> и сформулируйте, каких данных не хватает.</li></ol>\n<h2>Ограничения применения</h2>\n<p>Эта модель не даёт репрезентативную выборку. Она не измеряет cognitive cost, удовлетворённость, частоту обращений или стоимость поддержки. Она не заменяет user research, проверку доступности, нагрузочное тестирование и анализ прав. Она помогает не смешать вопросы и не выдать техническую запись за ответ на каждый из них.</p>\n<p>Учебные значения нельзя публиковать как результат внутреннего инструмента. В production-пути могут отличаться часы, задержка доставки, идентичность роли, источник ручной работы и правила хранения данных. Любое сравнение должно сохранять версию контракта и явно отмечать изменения границы.</p>\n<p>Показ владельца до отправки может снизить число вопросов о маршрутизации и не изменить очередь согласования. Ускорение одного этапа может увеличить нагрузку на другой. Поэтому изменение должно иметь narrow target и stop condition, а не обещание «сделать DX лучше».</p>\n<h2>Проверяемый критерий готовности</h2>\n<p>Разбор готов к инженерному решению, когда другой человек без устного пересказа может восстановить role, objective, expected result, stage order, source, wait bucket, UX-наблюдение, support signal, unknowns, owner, candidate change и comparison boundary. Он понимает, какой результат подтвердит гипотезу, а какой остановит вывод.</p>\n<p>Минимальная проверка даёт три наблюдаемых исхода. Корректная задача проходит структурную проверку и остаётся гипотезой до решения владельца. Неполный source, неверный порядок и forged effect claim возвращают <code>HOLD</code> с причиной. Ни один учебный вызов не отправляет данные и не меняет production. Только после этого можно подключать разрешённые источники и повторять тот же путь.</p>\n<h2>Проверяемые источники</h2><ul><li><a href=\"https://opentelemetry.io/docs/specs/otel/trace/api/\" target=\"_blank\" rel=\"noopener noreferrer\">OpenTelemetry: Tracing API</a> — официальная спецификация описывает spans, timestamps и events. Она помогает назвать техническую запись, но не определяет DX-score и не доказывает эффект изменения.</li><li><a href=\"https://www.gov.uk/service-manual/measuring-success/measuring-the-success-of-your-service\" target=\"_blank\" rel=\"noopener noreferrer\">GOV.UK Service Manual: Measuring the success of your service</a> — официальное руководство рекомендует сочетать performance metrics с user research, feedback и повторяемым измерением пути. Оно не задаёт пороги для конкретного внутреннего инструмента.</li><li><a href=\"https://www.w3.org/TR/performance-timeline/\" target=\"_blank\" rel=\"noopener noreferrer\">W3C Performance Timeline</a> — официальный стандарт задаёт примитивы для доступа к временным записям web-приложения. Он не превращает техническое время в доказательство понятности или удобства.</li></ul>"
}