Обратная связь

Шаблон запроса функции, становящийся changelog

5 мин чтения

Шаблон запроса функции — это форма с четырьмя вопросами: что пытается сделать человек, что его останавливает, что он попробовал вместо этого, и как он хочет быть уведомлён, когда это будет готово. Всё остальное, что обычно появляется на такой форме, селекторы приоритета, оценки усилий, баллы бизнес-ценности, предназначено для команды, получающей запрос, и неправильно заполняется отправителем.

Аккуратные запросы — неправильный тест для шаблона. Правильный: шесть месяцев спустя, когда функция выпущена, может ли кто-то найти запрос, понять его, и уведомить человека, который его написал? Большинство шаблонов спроектированы для приёма. Этот спроектирован для дня, когда петля замыкается.

Что должен включать шаблон запроса функции?

Он должен включать цель, блокер, обходной путь, и путь обратно к запросившему. Четыре поля, в этом порядке, каждое отвечает на вопрос, который команда задаст позже.

ПолеВопрос, на который отвечает позжеПочему оно в форме
Что вы пытаетесь сделать?Была ли построенная функция той, что нужна?Цель переживает любое конкретное предложение
Что вас останавливает сегодня?Как выглядит «готово»?Называет пробел, не предписывая исправление
Что вы делаете вместо этого?Насколько это на самом деле срочно?Болезненный обходной путь — более сильный сигнал, чем селектор приоритета
Как нам вас уведомить?Кто получает сообщение «выпущено»?Поле, которое чаще всего пропускают шаблоны

Что намеренно отсутствует: предложенное решение как обязательное поле (желанное в комментарии, неверное как рамка), селектор приоритета (каждый отправитель выбирает высокий), и любая оценка усилий или ценности (работа команды, после триажа). Шаблон, просящий решение, получает запросы на кнопки; шаблон, просящий цель, получает запросы на результаты, и о результатах пишется запись changelog.

Шаблон

Это шаблон issue GitHub, который мы используем, в виде формы. Вставьте его в .github/ISSUE_TEMPLATE/feature_request.yml, и он отрендерится как структурированная форма на странице нового issue. Запросы, поданные через него, попадают как issue с теми же полями, что и поданные через виджет обратной связи, что важно для следующего раздела.

name: Feature request
description: What you are trying to do, and what stops you.
labels: ["feature-request"]
body:
  - type: textarea
    id: goal
    attributes:
      label: What are you trying to do?
      description: >-
        The outcome, not the button. "Export a month of invoices as one
        PDF" beats "add a PDF export".
    validations:
      required: true
  - type: textarea
    id: blocker
    attributes:
      label: What stops you today?
      description: >-
        Where the product runs out. An error, a missing option, a limit.
    validations:
      required: true
  - type: textarea
    id: workaround
    attributes:
      label: What do you do instead?
      description: >-
        The spreadsheet, the script, the manual step. "Nothing, I gave
        up" is a valid answer.
  - type: input
    id: contact
    attributes:
      label: How should we tell you when it ships?
      description: >-
        An email address, or leave blank to be notified only on this
        issue.

Две детали выполняют работу. labels: ["feature-request"] означает, что запрос классифицируется при создании, вместо того чтобы ждать, пока кто-то его триажирует. А последнее поле существует, потому что «мы дадим вам знать» — это обещание, а обещанию нужен адрес.

Какие метки должен нести запрос функции?

Запрос функции должен нести одну метку для того, что он есть, одну для того, насколько он срочен, и одну для того, откуда он пришёл. Три метки, три оси, и каждую читает другая читательница.

МеткаЗначенияКто читает
Типfeature-request, bugКто решает, в какую очередь он попадает
Приоритетpriority:low, priority:medium, priority:highКто планирует следующий цикл
Источникfrom-widget, from-form, from-supportКто измеряет, откуда приходят запросы

Виджет применяет первые две оси и from-widget, когда подаёт отправку как issue; from-form и from-support предлагаются для запросов, приходящих другими путями. Метки виджета такие: тип (bug или feature-request, решаемый классификатором только по сообщению), приоритет (спокойный, конкретный отчёт о сбое высокий; дубликат чего-то уже спрошенного низкий; всё, что даже намекает на проблему безопасности, — bug и высокий, независимо от формулировки), и from-widget. Те же три оси работают для запросов, приходящих вручную через шаблон выше, и в этом суть: запрос — это запрос, независимо от того, откуда он вошёл.

Ещё одна конвенция: виджет удаляет адрес электронной почты отправителя из тела issue перед подачей, потому что issue живёт в репозитории, который может быть публичным, и заменяет его ссылкой на отправку. Адрес в issue не попадает; отправитель следит за результатом в самом виджете. Делайте то же самое с полем контакта, если ваш трекер виден людям вне команды.

Как запрос функции становится записью changelog?

Запрос функции становится записью changelog, когда pull request закрывает issue, а запись, составленная из этого pull request, связывается обратно. Механизм — собственные ключевые слова закрытия GitHub: PR, чьё описание говорит Fixes #142, закрывает issue 142 при merge. Если ваши записи changelog составляются из объединённых pull request, черновик может нести номер issue с собой, и запись знает, кто спрашивал.

Вот почему шаблон просит цель вместо решения. Когда запись пишется, цель — это предложение, нужное автору: «Теперь вы можете экспортировать месяц счетов как один PDF» — это запись changelog. «Добавлен экспорт PDF» — это сообщение коммита. Инструменты для changelog, составляющие записи из pull request, могут выполнить сбор и связь; формулировка всё ещё нуждается в человеке, и человеку нужна цель.

Что происходит, когда это выпущено?

Запросивший уведомляется со ссылкой на запись. В нашей настройке это происходит автоматически для запросов, пришедших через виджет: комментарий, говорящий «Shipped — <заголовок записи>» со ссылкой на опубликованную запись, размещённый на issue, как только человек одобряет запись, а виджет показывает отправителю ту же запись. Issue, поданный вручную по этому шаблону, автоматического комментария не получает; замыкайте эту петлю сами, по тому же правилу. Комментарий намеренно размещается при одобрении, а не при merge: комментарий, говорящий, что что-то в эфире до того, как это так, — это нарушенное обещание с временной меткой. Каждый запрос уведомляется не более одного раза; второе одобрение той же записи не создаёт второй комментарий.

Если вы делаете это вручную, применяется то же правило. Не замыкайте петлю из pull request. Замыкайте её из опубликованной записи, и замыкайте один раз. Лента и виджет несут ту же запись всем, кто не спрашивал, что большинство; комментарий для тех, кто спрашивал.

Почему большинство шаблонов запросов функций проваливаются

Они спроектированы, чтобы облегчить триаж, и им это удаётся, ценой единственного момента, важного для запросившего. Шаблон с двенадцатью полями получает меньше запросов, а те, что получает, приходят от людей с терпением заполнить двенадцать полей, что не та же популяция, что нуждается в функции. Шаблон с четырьмя полями, одно из которых «как с вами связаться», получает больше запросов и может почтить каждый из них.

FAQ

Должен ли шаблон запроса функции спрашивать о приоритете? Нет. Спрашивайте вместо этого об обходном пути. «Я экспортирую в таблицу и перепечатываю каждую пятницу» говорит о приоритете больше, чем выпадающий список, который отправитель поставил на высокий.

Должны ли запросившие предлагать решение? Могут, в свободном тексте. Не делайте это рамкой. Запросы, написанные как решения, труднее объединять друг с другом и труднее превращать в запись changelog.

Должны ли запросы функций появляться в публичной roadmap? После планирования да: метка на том же issue помещает его в колонку запланировано, и запросивший может видеть, как он движется. Статья публичная roadmap — это механизм.

Как обрабатывать дубликаты? Свяжите новый запрос с существующим issue и пометьте его низким приоритетом; не закрывайте его. Каждый дубликат — ещё один человек для уведомления при выпуске. С автоматическим комментарием Changeloop этот человек уведомляется, только если pull request называет и его issue (Fixes #142, fixes #187).

Где должен жить шаблон? В репозитории, который получит pull request, чтобы работало ключевое слово закрытия. Запрос в отдельном трекере должен связываться вручную при merge, и именно этот шаг пропускается.


Технические утверждения в этой статье не проходили независимую проверку. Если здесь что-то не так, сообщите нам, и мы исправим.

По теме на changeloop: Документация для разработчиков, Сравнение инструментов changelog

changeloop
Команда, которая делает changelog, замыкающий цикл. Пользователи о чём-то просят, ваша команда это делает, тот, кто просил, узнаёт об этом.