Перейти до змісту
Увійти

Теорія

Баг-репорт і GitHub issues: шаблони, мітки й майлстоуни

Як перетворити запит в один рядок на issues, з якими команда може працювати: як написати баг-репорт із кроками відтворення, запити на фічі з критеріями приймання, шаблон issue на GitHub, мітки, виконавці, sub-issues і майлстоун із дедлайном. А ще як ключові слова закриття й згадки пов'язують pull requests з issues і що кожен стан issue каже продакту.

~10 хв читання · 11 вправ · 250 балів

Перед початком

Один рядок від CEO

Понеділок третього тижня в Котокормі. Продуктова команда працює над балами лояльності, а робота розкидана по чатах, трьох pull requests і одному issue від CEO з назвою «Loyalty», де сказано лише, що це потрібно до Чорної п'ятниці.

Останній модуль — про планування роботи на GitHub. У першому уроці ти перетвориш запит в один рядок на issues, з якими команда може працювати: із шаблонами, мітками, відповідальними й майлстоуном із дедлайном.

Issues: запитання, яке йде перед кодом

У Котокормі робота над лояльністю розповзлася по чатах, трьох pull requests і одному issue, яке CEO назвав «Loyalty» й описав одним рядком: «Need this before Black Friday!!». Новий колега ніколи не знайшов би все це разом.

Issue — це місце на GitHub для всього, про що команда має знати або що має зробити. Більшість issues належать до трьох видів: баг-репорт (щось зламалося), запит на фічу (чогось бракує) і задача (справа з відповідальним і часто з датою). Issue зазвичай з'являється раніше за код, і читають його люди, яких не було на нараді, тож воно має бути зрозумілим без пояснень.

Issues у catchow/website, понеділок зранку
  • Loyalty#147

    Відкрив CEO минулого тижня · без міток і виконавця

  • Test an orange checkout button#135

    Мітка idea · відкрив Daniel

  • Checkout button stays grey after editing cart#131

    Звіт про баг від підтримки · закрито через #134

  • Show prices in euros#138

    Запит на фічу · закрито як незаплановане

Код, який відповідає на issue, приходить пізніше як pull request із посиланням на нього. Issue — це запитання, pull request — одна з відповідей. Pull request можна відкрити й без issue, але за місяць ніхто не згадає, яку проблему він розв'язував. Закрите issue теж зберігає причину: закрито як виконане або закрито як незаплановане.

  • Так
    Issue #131 «Checkout button stays grey after editing cart» — баг-репорт; pull request #134 його виправляє й посилається на нього.

    Проблема і її виправлення лишаються пов'язаними.

  • Не так
    Гілка fix-thing і повідомлення в чаті: «виправив той баг із вівторка».

    За місяць ніхто не знає, яку проблему це розв'язало.

Баг-репорт, який повторить будь-хто

Розробник може виправити лише те, що здатен відтворити на своєму екрані. Добрий баг-репорт дає йому чотири речі.

  • Кроки, щоб відтворити: що робити й у якому порядку, починаючи з відомого стану (який акаунт, який флаг увімкнено).
  • Очікуваний результат: що мало статися.
  • Фактичний результат: що сталося натомість.
  • Де: пристрій, браузер, акаунт, номер замовлення, а якщо є — скриншот.

Issue #159 у Котокормі зроблено саме так. З акаунта працівника замовляють товари на 600 грн, застосовують промокод на 100 грн і платять 500 грн. Очікувано: 50 балів, по одному за кожні 10 грн оплати. Фактично: 60 балів, пораховані до знижки. Будь-хто повторить це за дві хвилини, а два числа одразу показують розбіжність.

Людина йде намальованими на підлозі слідами до прилавка, де поруч лежать дві купки монет помітно різної висоти.
Кроки, очікуваний і фактичний результат: проблему видно будь-кому за дві хвилини.

Назва описує симптом, а не емоцію: «Points counted before promo discount», а не «Points broken». Двом речам у репорті не місце. Терміновість узгоджують із командою, а не пишуть великими літерами. А шукати файл із помилкою — робота розробника; здогадка автора репорту лише збиває з пантелику.

  • Так
    «Points counted before promo discount. Кроки: акаунт працівника, кошик на 600 грн, промокод 100 грн, оплата 500 грн. Очікувано: 50 балів. Фактично: 60. Де: десктоп, Chrome, замовлення 58213.»

    Будь-хто повторить і побачить розбіжність.

  • Не так
    «Points broken. Клієнт каже, що бали неправильні, виправте терміново!!»

    Нічого відтворити й нічого порівняти.

  • Не так
    «ТЕРМІНОВО: баг, мабуть, у файлі оформлення замовлення.»

    Кричить про терміновість і вгадує код замість описати, що сталося.

Запити на фічі й шаблони issues

Запит на фічу відповідає на три запитання для тих, кого не було на нараді: чого хочуть, навіщо це і як зрозуміти, що роботу зроблено. У корисному запиті є:

  • назва, що описує результат, а не тему: «Customers can reuse a saved delivery address», а не «Addresses»;
  • кілька рядків контексту: у кого проблема і чого вона їм коштує;
  • критерії приймання: перевірки, які мають справджуватися, коли роботу завершено, сформульовані так, щоб людина могла їх перевірити;
  • що не входить у межі, щоб робота непомітно не розросталася.

GitHub може підштовхнути всіх до такого формату через шаблони issues: файли в папці .github/ISSUE_TEMPLATE/ репозиторію. Після цього New issue спершу питає, що саме відкрити, наприклад «Bug report» чи «Feature request», і заповнює заголовки й мітки; шаблон бага може сам додати мітку bug. Є й версія-форма з окремими полями, і деякі з них можна зробити обов'язковими.

Пам'ятай про межу: шаблон може вимагати поле, але не добру відповідь. Заповнити його добре — все одно завдання автора.

  • Так
    Критерій приймання: «Збережену адресу можна вибрати під час оформлення, не вводячи її заново.»

    Людина перевірить це за хвилину.

  • Не так
    Критерій приймання: «Адреси добре працюють.»

    Ніхто не скаже, коли це справдилося.

  • Так
    Шаблон «Bug report» із заголовками Steps to reproduce, Expected, Actual і Where, який сам додає мітку bug.

    Заголовки підказують, що потрібно розробнику.

Мітки й виконавці

Бокова панель праворуч від issue має ті самі поля, що й pull request, і кожне відповідає на одне з повсякденних запитань продакта.

  • Labels (мітки) кажуть, що це за робота і якої частини продукту стосується: bug, content, loyalty. Відфільтруй список issues за label:loyalty, і вся програма поміститься на одному екрані.
  • Assignees (виконавці) — ті, хто відповідає за те, щоб issue рухалося. Це не обов'язково автор: продакт пише баг #159, а виконавцем призначають розробника Daniel. Призначити можна кількох людей, але одне ім'я одразу показує, кого питати.
  • Milestone прив'язує issue до цілі з датою.
  • Development показує гілки й pull requests, які над ним працюють.

Міток має бути мало, і кожна має щось означати. Якщо на кожному issue по шість міток, фільтр за будь-якою нічого не скаже, і ними перестануть користуватися. Невеликого узгодженого набору (тип роботи плюс частина продукту) більшості команд досить.

  • Так
    #159: мітки bug і loyalty, виконавець Daniel.

    Тип, частина продукту й один відповідальний.

  • Не так
    Кожне issue отримує bug, urgent, frontend, loyalty, q4 і important.

    Коли позначено все, фільтр нічого корисного не показує.

  • Не так
    Виконавець: той, хто випадково написав issue.

    Автор не завжди той, хто рухає справу далі.

Sub-issues: батьківське issue і його частини

Деяка робота завелика для одного issue. Програмі лояльності Котокорму потрібні виправлення бага, баланс у кабінеті, списання балів під час оформлення, сторінка програми й перемикач запуску. Десять не пов'язаних між собою issues знову все розпорошать, а один величезний опис сховає, хто що робить.

Рішення — батьківське issue із sub-issues. У батьківському — мета й те, що означає «запущено»: #147 перейменовують на «Launch loyalty points before Black Friday». Кожне sub-issue — звичайне issue, прикріплене до нього, з власним номером, виконавцем, мітками й станом, тож Daniel може закрити свою частину, не чіпаючи чужих. Батьківське issue показує їх списком і рахує прогрес, наприклад «закрито 1 з 4».

Великий повітряний змій летить високо, до його хвоста прив'язано кілька менших, і мотузку кожного тримає інша людина.

У старіших репозиторіях роблять щось схоже чеклистом в описі: рядки, що починаються з - [ ], показуються як прапорці. Це працює, але їх ставлять вручну, а в рядка чеклиста немає власного виконавця чи стану. Sub-issues — це ті частини, які люди справді беруть у роботу.

  • Так
    Батьківське #147 «Launch loyalty points before Black Friday» із sub-issues #159, #162, #163 і #164, у кожного свій відповідальний.

    Мета вгорі, частини, які можна взяти, нижче.

  • Не так
    Десять окремих issues від «Loyalty 1» до «Loyalty 10», ні з чим не пов'язані.

    Ніхто не бачить усієї програми та її прогресу.

Майлстоуни й смуга прогресу

Майлстоун об'єднує issues і pull requests під однією метою, з необов'язковим дедлайном і смугою прогресу. Мітки відповідають на запитання «що це за робота?», а майлстоун — «до чого ми йдемо і до коли?». Майлстоун Котокорму «Loyalty launch» має дедлайн 13 листопада, за два тижні до Black Friday, і містить батьківське #147, його sub-issues і pull requests лояльності.

Смуга — проста арифметика: закриті елементи ÷ усі елементи. Змерджений pull request рахується як закритий. Так само й issue, закрите як незаплановане: відкинута ідея рухає смугу так само, як завершена робота. Коли закрито 2 елементи з 8, «Loyalty launch» показує 25%.

Майлстоун «Loyalty launch» · Due by November 13GitHub показує «25% complete»: 2 закриті, 6 відкритих.
  • Launch loyalty points before Black Friday#147

    Батьківське · закрито 0 з 4 sub-issues · Sofia

  • Add loyalty points behind a flag#155

  • Earn points on paid orders#156

  • Write loyalty programme page#157

    Emma

  • Points counted before promo discount#159

    bug, loyalty · Daniel

  • Show points balance in the account#162

    loyalty · Daniel

  • Spend points at checkout#163

    loyalty · Daniel

  • Switch on loyalty points for all customers#164

    loyalty · Sofia

Через це відсоток легко прочитати неправильно. Він рахує елементи, а не зусилля: виправлення одного рядка тексту і вся зміна оформлення замовлення важать однаково. І половинок у ньому немає: відкритий pull request просто відкритий. Перш ніж звітувати про прогрес, прочитай список під смугою: що ще відкрито, наскільки воно велике і чи не закрито щось як відкинуте замість зробленого.

Смуга прогресу з чотирьох однакових блоків, один заповнено; за порожніми блоками лежать валуни зовсім різного розміру.
Читай список під смугою, а не лише частку виконаного.
  • Так
    «Закрито 60% елементів, але один із них відкинули, а найбільша частина, набори, ще відкрита.»

    Читає список, а не лише смугу.

  • Не так
    «Майлстоун на 60%, тож ми зробили більше половини.»

    Смуга рахує елементи, а не зусилля, і враховує відкинуті.

Ключові слова закриття й згадки

Робота над issue може початися з самого issue. У розділі Development є Create a branch: GitHub пропонує назву з номера й заголовка issue, а Daniel змінює її за звичкою команди на fix/points-after-discount. Відтак issue показує цю гілку, а згодом і її pull request.

В описі pull request два посилання схожі на вигляд, але поводяться по-різному:

  • Fixes #159 містить ключове слово закриття. Коли pull request змерджать в основну гілку (main), GitHub закриє #159 як виконане. Ключові слова — close, fix і resolve з їхніми звичними закінченнями: closes, closed, fixes, fixed, resolves, resolved.
  • Part of #147 — проста згадка. У хронології #147 з'явиться позначка, що pull request на нього посилався, і нічого не закриється. «Related to» чи «See» працюють так само.
Від issue #159 до mainГілку створено просто з issue, тож #159 показує її в розділі Development. Коли #165 змерджать через squash, його коміт у main закриє #159.
  1. e5f81a3Hide loyalty menu link when flag is off (#158)v1.4.1
  2. b2e6f10Earn points on paid orders (#156)
  3. 6e0b4d8Add issue templates (#160)
  4. 4f8c2e1Count points from the paid amountfix/points-after-discount
  5. 7a1d9c3Count points after discounts (#165)main

Граф комітів, від найстарішого: Від issue #159 до main 1. коміт e5f81a3 у main "Hide loyalty menu link when flag is off (#158)", тег v1.4.1 2. коміт b2e6f10 у main "Earn points on paid orders (#156)" 3. коміт 6e0b4d8 у main "Add issue templates (#160)" 4. гілка fix/points-after-discount починається від коміту 6e0b4d8 5. коміт 4f8c2e1 у fix/points-after-discount "Count points from the paid amount" 6. коміт 7a1d9c3: fix/points-after-discount змерджено в main через squash "Count points after discounts (#165)" (виділено)

Саме тому частина великої програми ніколи не пише Fixes #147: перший змерджений шматок закрив би все батьківське issue. Закривай своє sub-issue, батьківське лише згадуй.

Fixes #159 у pull request #165 закриває баг після мерджу в main; Part of #147 лише згадує батьківське issue, і воно лишається відкритим.
  • Так
    Fixes #159. Part of #147.

    Закриває баг, батьківське лише згадує.

  • Не так
    Fixes #147 у pull request для однієї частини програми.

    Перша ж змерджена частина закрила б усю програму.

Одне ключове слово на issue, закриття лише після мерджу

У ключових слів закриття є два правила, на яких часто спотикаються.

Одне ключове слово на кожен номер. Fixes #162 and #163 закриє лише #162; #163 отримає згадку й лишиться відкритим. Щоб закрити обидва, повтори ключове слово: Fixes #162, fixes #163. А батьківське issue додай простою згадкою, Part of #147, щоб ця частина з'явилася в хронології програми, але не закрила її.

Нічого не закривається до мерджу. Відкритий pull request із Fixes #159 лише пов'язує їх; issue закриється, коли pull request змерджать в основну гілку, у Котокормі це main. Pull request, змерджений в іншу гілку, нічого не закриває. Ті самі ключові слова працюють і в повідомленні коміту, щойно цей коміт потрапить в основну гілку.

Після мерджу issue отримує фіолетову позначку «закрито як виконане», а розділ Development зберігає посилання на pull request, тож будь-хто бачить, що його закрило і коли. Якщо виправлення не спрацювало, issue можна відкрити знову, а не заводити нове.

  • Так
    Fixes #162, fixes #163. Part of #147.

    Два ключові слова для двох sub-issues, згадка для батьківського.

  • Не так
    Fixes #162 and #163.

    Закриється лише #162; #163 випадково лишиться відкритим.

  • Так
    Виправлення не пройшло тест працівниками: знову відкрити #159 і пов'язати новий pull request.

    Історія лишається в одному місці.

Закрито — ще не означає, що клієнти це бачать

Коли змерджено #165, pull request від Daniel з Fixes #159 для бага з балами, #159 закривається само й отримує фіолетову позначку: закрито як виконане. У Котокормі мердж у main викладають за кілька хвилин, і тест працівників після обіду проходить: оплачено 500 грн, нараховано 50 балів. Баг виправлено в робочому коді.

Але клієнти досі не бачать жодних балів. Програма схована за фіча-флагом loyalty-points, а він увімкнений лише для працівників. Стан issue і флаг відповідають на різні запитання:

  • Стан issue (відкрите, закрите як виконане, закрите як незаплановане): чи зроблено роботу?
  • Деплой: чи працює код на сайті?
  • Фіча-флаг: хто бачить фічу?

Тримай ці три речі окремо, коли звітуєш підтримці, продажам чи CEO. «#159 закрито» — правда, але вона вводить в оману, якщо слухач почує «клієнти тепер отримують правильні бали». Кажи, що вже працює, хто це бачить і коли це зміниться. А сторінка релізу — лише опис змін; її публікація нічого не викладає.

  • Так
    «Виправлення вже на сайті. Клієнти побачать бали, коли ми ввімкнемо флаг для всіх; це заплановано до 13 листопада.»

    Розділяє «зроблено», «викладено» і «видно».

  • Не так
    «#159 закрито, тож клієнти вже отримують правильні бали.»

    У клієнтів ще взагалі немає балів: флаг увімкнено лише для працівників.

Готові потренуватися?

Виконайте вправи уроку й отримайте 250 балів, коли пройдете його вперше.

Розпочати вправи