Bucla de feedback

Șablon de cerere de funcție care devine changelog

6 min de citit

Un șablon de cerere de funcție e un formular cu patru întrebări: ce încearcă persoana să facă, ce o oprește, ce a încercat în loc de asta, și cum vrea să fie anunțată când e gata. Tot restul care apare de obicei pe unul, selectoare de prioritate, estimări de efort, scoruri de valoare de business, e pentru echipa care primește cererea, și e completat greșit de cel care o trimite.

Cererile ordonate sunt testul greșit pentru un șablon. Cel corect: șase luni mai târziu, când funcția e lansată, poate cineva găsi cererea, s-o înțeleagă, și să anunțe persoana care a scris-o? Majoritatea șabloanelor sunt proiectate pentru admisie. Ăsta e proiectat pentru ziua în care se închide bucla.

Ce ar trebui să includă un șablon de cerere de funcție?

Ar trebui să includă obiectivul, blocajul, soluția temporară, și o cale înapoi la solicitant. Patru câmpuri, în această ordine, fiecare răspunde la o întrebare pe care echipa o va pune mai târziu.

CâmpÎntrebarea la care răspunde mai târziuDe ce e pe formular
Ce încerci să faci?Funcția construită a fost cea necesară?Obiectivul supraviețuiește oricărei propuneri concrete
Ce te oprește azi?Cum arată “gata”?Numește lipsa fără a prescrie corecția
Ce faci în loc de asta?Cât de urgent e cu adevărat?O soluție temporară dureroasă e un semnal mai puternic decât un selector de prioritate
Cum ar trebui să te anunțăm?Cine primește mesajul “lansat”?Câmpul pe care majoritatea șabloanelor îl omit

Ce lipsește deliberat: o soluție propusă ca un câmp obligatoriu (binevenită ca un comentariu, greșită ca un cadru), un selector de prioritate (fiecare persoană care trimite alege ridicat), și orice estimare de efort sau valoare (treaba echipei, după triaj). Un șablon care cere o soluție primește cereri de butoane; un șablon care cere un obiectiv primește cereri de rezultate, iar despre rezultate se scrie o intrare de changelog.

Șablonul

Ăsta e șablonul de issue GitHub pe care-l folosim, ca formular. Lipiți-l în .github/ISSUE_TEMPLATE/feature_request.yml și se randă ca un formular structurat pe pagina de issue nou. Cererile depuse prin el ajung ca issue-uri cu aceleași câmpuri ca cele depuse dintr-un widget de feedback, ceea ce contează pentru secțiunea următoare.

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.

Două detalii fac treaba. labels: ["feature-request"] înseamnă că cererea e clasificată la creare în loc să aștepte ca cineva s-o trieze. Iar ultimul câmp există pentru că “vă vom anunța” e o promisiune, și o promisiune are nevoie de o adresă.

Ce etichete ar trebui să poarte o cerere de funcție?

O cerere de funcție ar trebui să poarte o etichetă pentru ce este, una pentru cât de urgentă e, și una pentru de unde vine. Trei etichete, trei axe, și fiecare e citită de o cititoare diferită.

EtichetăValoriCine o citește
Tipfeature-request, bugCine decide în ce coadă intră
Prioritatepriority:low, priority:medium, priority:highCine planifică următorul ciclu
Sursăfrom-widget, from-form, from-supportCine măsoară de unde vin cererile

Widget-ul aplică primele două axe și from-widget atunci când depune o trimitere ca issue; from-form și from-support sunt sugestii pentru cererile care vin pe alte căi. Etichetele widget-ului sunt un tip (bug sau feature-request, decis de un clasificator doar din mesaj), o prioritate (un raport de eroare calm, specific e ridicat; un duplicat al a ceva deja cerut e scăzut; orice sugerează măcar o problemă de securitate e bug și ridicat, indiferent de formulare), și from-widget. Aceleași trei axe funcționează pentru cererile care vin manual prin șablonul de mai sus, și ăsta e punctul: o cerere e o cerere, indiferent pe unde a intrat.

Încă o convenție: widget-ul elimină adresa de e-mail a persoanei care trimite din corpul issue-ului înainte de a-l depune, pentru că issue-ul trăiește într-un repository care poate fi public, și o înlocuiește cu o referință de trimitere. Adresa rămâne în afara issue-ului; persoana care a trimis urmărește rezultatul în widget. Faceți același lucru cu câmpul de contact dacă tracker-ul vostru e vizibil pentru oameni din afara echipei.

Cum devine o cerere de funcție o intrare de changelog?

O cerere de funcție devine o intrare de changelog când un pull request închide issue-ul, iar intrarea redactată din acel pull request leagă înapoi. Mecanismul sunt propriile cuvinte cheie de închidere ale GitHub: un PR a cărui descriere spune Fixes #142 închide issue-ul 142 la merge. Dacă intrările voastre de changelog sunt redactate din pull request-uri integrate, ciorna poate purta numărul issue-ului cu ea, iar intrarea știe cine a cerut.

Ăsta e motivul pentru care șablonul cere obiectivul în loc de soluție. Când intrarea e scrisă, obiectivul e propoziția de care are nevoie scriitorul: “Acum puteți exporta o lună de facturi ca un singur PDF” e o intrare de changelog. “Adăugat export PDF” e un mesaj de commit. Uneltele de changelog care redactează din pull request-uri pot face colectarea și link-ul; formularea încă are nevoie de o persoană, iar persoana are nevoie de obiectiv.

Ce se întâmplă când e lansat?

Solicitantul e anunțat, cu un link către intrare. În configurarea noastră asta e automat pentru cererile venite prin widget: un comentariu care spune “Shipped — <titlul intrării>” cu un link către intrarea publicată, postat pe issue de îndată ce o persoană aprobă intrarea, în timp ce widget-ul îi arată celui care a trimis aceeași intrare. Un issue depus manual din acest șablon nu primește niciun comentariu automat; închideți voi acea buclă, după aceeași regulă. Comentariul e postat deliberat la aprobare, nu la merge: un comentariu care spune că ceva e live înainte de a fi e o promisiune ruptă cu marcaj temporal. Fiecare cerere e notificată cel mult o dată; o a doua aprobare a aceleiași intrări nu produce un al doilea comentariu.

Dacă faceți asta manual, aceeași regulă se aplică. Nu închideți bucla din pull request. Închideți-o din intrarea publicată, și închideți-o o dată. Fluxul și widget-ul poartă aceeași intrare tuturor celor care n-au cerut, care sunt majoritatea; comentariul e pentru cei care au cerut.

De ce eșuează majoritatea șabloanelor de cerere de funcție

Sunt proiectate să ușureze triajul și reușesc, cu prețul singurului moment care contează pentru solicitant. Un șablon cu douăsprezece câmpuri primește mai puține cereri, iar cele pe care le primește provin de la oameni cu răbdarea de a completa douăsprezece câmpuri, ceea ce nu e aceeași populație ca cea care are nevoie de funcție. Un șablon cu patru câmpuri, dintre care unul e “cum vă contactăm”, primește mai multe cereri și le poate onora pe toate.

FAQ

Ar trebui un șablon de cerere de funcție să întrebe despre prioritate? Nu. Întrebați în schimb despre soluția temporară. “Export într-o foaie de calcul și retastez în fiecare vineri” spune mai mult despre prioritate decât un meniu derulant pe care persoana care a trimis l-a pus pe ridicat.

Ar trebui solicitanții să propună o soluție? Pot, în textul liber. Nu faceți din asta cadrul. Cererile scrise ca soluții sunt mai greu de fuzionat unele cu altele și mai greu de transformat într-o intrare de changelog.

Ar trebui cererile de funcții să apară pe o roadmap publică? Odată planificate, da: o etichetă pe același issue îl pune în coloana planificat, iar solicitantul poate vedea cum se mișcă. Articolul roadmap publică e mecanismul.

Cum gestionez duplicatele? Legați cererea nouă de issue-ul existent și etichetați-o cu prioritate scăzută; nu-l închideți. Fiecare duplicat e încă o persoană de anunțat când e lansat. Cu comentariul automat al Changeloop, acea persoană e anunțată doar dacă pull request-ul numește și issue-ul ei (Fixes #142, fixes #187).

Unde ar trebui să trăiască șablonul? În repository-ul care va primi pull request-ul, ca să funcționeze cuvântul cheie de închidere. O cerere într-un tracker separat trebuie legată manual la merge, și ăsta e pasul care e sărit.


Afirmațiile tehnice din acest articol nu au fost verificate independent. Dacă ceva nu e corect, spune-ne și vom corecta.

Pe changeloop: Documentație pentru dezvoltatori, Comparație instrumente changelog

changeloop
Echipa care construiește un changelog care închide bucla. Utilizatorii cer ceva, echipa ta livrează, cel care a cerut află.