← Valtive QA Quiz quiz.valtive.io/notes/reporting valtive.io
📖 Про цей гайд📖 About this guide
Для когоWho it's for
Для QA інженерів, яких на співбесіді питають про тест-звіти, дашборди й метрики якості: від «що таке pass rate» на Junior до «менеджмент хоче 100% зелених тестів» на Senior. Як писати сам баг-репорт, розібрано в окремому гайді Bug Reporting — тут мова про те, що з тестів і дефектів збирається у звіти, цифри та релізні рішення.
For QA engineers who get asked about test reports, dashboards and quality metrics in interviews: from «what is pass rate» at Junior to «management wants 100% green tests» at Senior. How to write the bug report itself lives in the separate Bug Reporting guide; this one is about what tests and defects turn into — reports, numbers and release decisions.
Як користуватисьHow to use it
Фільтруй картки за рівнем, відкривай і дивись на артефакт: формулу з підрахунком, звіт, запит чи повідомлення стейкхолдерам — саме такі речі просять показати на співбесіді. Потім закрий картку й розкажи відповідь вголос за блоком 🗣 Як читати вголос: він звучить як жива розмова, а не як конспект.
Filter the cards by level, open one and look at the artefact: a formula with a worked calculation, a report, a query or a message to stakeholders — exactly what interviewers ask you to show. Then close the card and give the answer out loud using the 🗣 How to say it out loud block: it sounds like a real conversation, not like notes.
Три якорі — з них випливає більшість відповідейThree anchors — most answers follow from them
ЯКІР 1 · Звіт веде до рішенняANCHOR 1 · A report drives a decision
Звіт, після якого ніхто нічого не вирішує, — зайвий. Перший рядок — висновок і ризик, цифри йдуть далі як доказ.
A report nobody decides anything from is waste. The first line is the conclusion and the risk; the numbers follow as evidence.
ЯКІР 2 · У метрики є знаменникANCHOR 2 · Every metric has a denominator
Відсоток без «від чого» нічого не значить. Назви формулу, джерело даних і тренд — а не одну цифру на один день.
A percentage without «of what» means nothing. Name the formula, the data source and the trend, not a single number for a single day.
ЯКІР 3 · QA рекомендує, бізнес вирішуєANCHOR 3 · QA recommends, the business decides
QA не «пропускає» реліз, а чесно описує залишковий ризик. Метрика, яку перетворили на ціль, починає брехати.
QA does not «let a release through»; it states the residual risk honestly. A metric turned into a target starts to lie.
49
питаньquestions
—
Junior
—
Middle
—
Senior
—
voice-блокиvoice blocks
📚 5 РОЗДІЛІВ📚 5 SECTIONS
0 / 0 · розгорни картку, щоб зарахувати їїopen a card to count it
▶ 1 9
1 Що таке тест-звіт і які види звітів готує QA-команда? What is a test report, and which kinds of reports does a QA team produce?
Звіти про тестування і статусTest reports and status updates Junior ▶

Що це: Тест-звіт — документ або повідомлення, яке переводить сирі результати тестування у відповідь на питання «що ми знаємо про якість цієї збірки і що з цим робити». На практиці їх кілька: щоденний статус (прогрес і блокери), test execution report (що запускали і з яким результатом), test summary або test completion report наприкінці спринту чи релізу, і test incident report про окрему аномалію.

Навіщо і коли: Різні люди приймають різні рішення. Розробнику зранку потрібен список нових падінь, тімліду — чи встигаємо до кінця спринту, продакт-менеджеру перед релізом — чи можна випускати і з яким ризиком. Один звіт «для всіх» зазвичай не підходить нікому.

Приклад з проєкту: У команді мобільного банкінгу ми мали три формати: автоматичне повідомлення в Slack після нічного прогону, щотижневий короткий звіт для тімліда і один test summary перед кожним релізом у стор. Коли продакт попросив «ще один звіт», ми спершу спитали, яке рішення він з нього прийматиме, — і виявилося, що вистачить одного рядка в релізному звіті.

Підводні камені: Звіт перетворюється на таблицю з сотнями рядків, де висновок треба шукати самому. Або звіт пишуть «для галочки» і ніхто його не відкриває.

Уточнююче питання: «Хто читає ваш звіт і що він з ним робить?» — назви конкретного адресата і рішення, яке він приймає.

What it is: A test report is a document or message that turns raw test results into an answer to «what do we know about the quality of this build, and what should we do about it». In practice there are several kinds: the daily status (progress and blockers), the test execution report (what ran and with what result), the test summary or test completion report at the end of a sprint or release, and the test incident report about a single anomaly.

Why and when: Different people make different decisions. A developer needs the list of new failures in the morning, a team lead needs to know whether the sprint is on track, and a product manager needs to know before release whether it can ship and with what risk. One report «for everyone» usually serves nobody.

From a project: On a mobile banking team we had three formats: an automatic Slack message after the nightly run, a short weekly report for the team lead and one test summary before every app-store release. When the product owner asked for «one more report», we first asked which decision it would support, and it turned out one line in the release report was enough.

Pitfalls: The report grows into a table with hundreds of rows where readers have to dig for the conclusion themselves. Or it is written as a formality and nobody opens it.

Follow-up question: «Who reads your report and what do they do with it?» Name a specific reader and the decision they make.

Карта звітів: хто читає і коли
Report map: who reads it and when
Звіт                   Коли                  Читач               Рішення
---------------------  --------------------  ------------------  -----------------------------
Щоденний статус        щодня, 10:00          команда, тімлід     що розблокувати сьогодні
Test execution report  після кожного прогону розробники          що лагодити першим
Test summary report    кінець спринту/релізу PO, реліз-менеджер  випускати чи ні, з яким ризиком
Test incident report   при аномалії          розробник, тімлід   чи це дефект, хто розбирає
Report                 When                  Reader              Decision
---------------------  --------------------  ------------------  ------------------------------
Daily status           daily, 10:00          team, team lead     what to unblock today
Test execution report  after every run       developers          what to fix first
Test summary report    end of sprint/release PO, release manager ship or not, with what risk
Test incident report   on an anomaly         developer, lead     is it a defect, who owns it
🔧 Що в артефакті: Кожен рядок прив'язує формат звіту до конкретного читача і рішення. Якщо для звіту не вдається заповнити останню колонку, його, найімовірніше, можна не писати.
🔧 What the artefact shows: Each row ties a report format to a specific reader and a decision. If you cannot fill in the last column for a report, you can probably stop writing it.
💡 Простими словами: Звіт — це не архів результатів, а коротка відповідь на питання конкретної людини.
💡 In plain words: A report is not an archive of results; it is a short answer to one person's question.
🗣 Як читати вголос Я розрізняю звіти за тим, хто їх читає. Розробникам після прогону я даю список нових падінь з посиланнями на trace. Тімліду раз на день пишу, де ми є і що блокує. А перед релізом збираю test summary, де першим рядком стоїть рекомендація і ризик. Коли мене просять про новий звіт, я спершу питаю, яке рішення на ньому прийматимуть, — часто виявляється, що досить додати рядок в уже наявний.
🗣 How to say it out loud I tell reports apart by who reads them. Developers get a list of new failures with trace links after each run. The team lead gets a daily note on where we are and what is blocking us. Before a release I put together a test summary where the first line is the recommendation and the risk. When someone asks me for a new report, I first ask what decision it will support, and quite often adding one line to an existing report does the job.
2 Які розділи має test summary report і що писати в кожному? Which sections does a test summary report have, and what goes into each one?
Звіти про тестування і статусTest reports and status updates Junior ▶

Що це: Test summary report (у ISO/IEC/IEEE 29119-3 — test completion report) підсумовує тестування за спринт, реліз чи етап. Типові розділи: висновок і рекомендація, обсяг (що тестували і що свідомо ні), середовище і збірка, результати виконання, відкриті дефекти за severity, виконання exit criteria, ризики і known issues.

Навіщо і коли: Його пишуть, коли етап завершується і хтось має вирішити, чи рухатися далі. Документ також лишається історією: через пів року можна подивитися, що саме перевіряли в версії 3.4 і з якими відомими проблемами її випустили.

Приклад з проєкту: Для релізу 3.4 інтернет-магазину звіт почався з одного речення: «Рекомендуємо реліз за умови, що фіча Apple Pay лишається вимкненою прапорцем». Далі — 412 кейсів, 401 пройдено, 6 впали, 5 заблоковані, два відкриті Major з обхідним шляхом. Реліз-менеджеру вистачило першого абзацу.

Підводні камені: Висновок у кінці документа, куди ніхто не доходить. Відсутній розділ «що не тестували»: без нього читач думає, що покрито все. Цифри без збірки і дати — незрозуміло, до чого вони відносяться.

Уточнююче питання: «Що ви пишете, якщо тестування не завершене, а дедлайн настав?» — чесно вказую непокриті області як ризик і не підганяю цифри.

What it is: A test summary report (called a test completion report in ISO/IEC/IEEE 29119-3) sums up testing for a sprint, release or phase. Typical sections: conclusion and recommendation, scope (what was tested and what deliberately was not), environment and build, execution results, open defects by severity, status of exit criteria, risks and known issues.

Why and when: It is written when a phase ends and someone has to decide whether to move on. It also stays as history: six months later you can check exactly what was covered in version 3.4 and which known problems it shipped with.

From a project: For release 3.4 of an online shop the report opened with one sentence: «We recommend releasing provided the Apple Pay feature stays switched off behind its flag». Then came 412 cases, 401 passed, 6 failed, 5 blocked, and two open Major bugs with a workaround. The release manager only needed the first paragraph.

Pitfalls: The conclusion sits at the end of the document where nobody gets to it. There is no «not tested» section, so readers assume everything was covered. Numbers come without a build and a date, so it is unclear what they refer to.

Follow-up question: «What do you write if testing is not finished and the deadline has arrived?» I list the uncovered areas honestly as a risk and do not massage the numbers.

Test summary report для релізу
Test summary report for a release
TEST SUMMARY REPORT · Shop Web 3.4.0 · build 3.4.0-rc2 · 2026-09-18
Висновок: GO з умовою — Apple Pay лишається вимкненим (flag applepay_v2=off)

Обсяг:       кошик, checkout, промокоди, профіль, нова сторінка доставки
Не тестували: Apple Pay (сертифікат sandbox недоступний), IE-режим Edge
Середовище:  staging-eu, Chrome 129, Firefox 130, Safari 17, iOS 17/Android 14

Виконання    План  Пройдено  Впало  Заблоковано  Не запускали
Регрес       340   333       4      3            0
Нові фічі     72    68       2      2            0
Разом        412   401       6      5            0

Відкриті дефекти: Blocker 0 · Critical 0 · Major 2 (є обхід) · Minor 7
Exit criteria: 4 з 5 виконано (покриття вимог 96% при цілі 95%; Apple Pay — ні)
Ризики: SHOP-2211 у Safari повільно оновлюється кошик (до 3 с)
TEST SUMMARY REPORT · Shop Web 3.4.0 · build 3.4.0-rc2 · 2026-09-18
Conclusion: GO with a condition — Apple Pay stays off (flag applepay_v2=off)

Scope:       cart, checkout, promo codes, profile, new delivery page
Not tested:  Apple Pay (sandbox certificate unavailable), Edge IE mode
Environment: staging-eu, Chrome 129, Firefox 130, Safari 17, iOS 17/Android 14

Execution    Planned  Passed  Failed  Blocked  Not run
Regression   340      333     4       3        0
New features  72       68     2       2        0
Total        412      401     6       5        0

Open defects: Blocker 0 · Critical 0 · Major 2 (workaround) · Minor 7
Exit criteria: 4 of 5 met (requirements coverage 96% vs 95% target; Apple Pay not met)
Risks: SHOP-2211 cart refresh in Safari is slow (up to 3 s)
🔧 Що в артефакті: Висновок стоїть у другому рядку, поруч зі збіркою і датою. Розділ «Не тестували» так само важливий, як результати: він пояснює, де саме лишився ризик.
🔧 What the artefact shows: The conclusion is on the second line, next to the build and date. The «Not tested» section matters as much as the results because it shows exactly where the risk remains.
🗣 Як читати вголос Test summary я будую згори донизу за важливістю. Спочатку рекомендація одним реченням, потім що тестували і, головне, що не тестували, потім цифри виконання і відкриті дефекти за severity, в кінці ризики. Якщо реліз-менеджер прочитає тільки перший абзац, він уже знатиме, чи випускати і з якою умовою. І я завжди пишу номер збірки — цифри без збірки через тиждень нічого не значать.
🗣 How to say it out loud I build a test summary top-down by importance. First the recommendation in one sentence, then what was tested and, crucially, what was not, then the execution numbers and open defects by severity, and the risks at the end. If the release manager reads only the first paragraph, they already know whether to ship and under which condition. And I always include the build number, because numbers without a build mean nothing a week later.
3 Чим test execution report відрізняється від test summary report? How is a test execution report different from a test summary report?
Звіти про тестування і статусTest reports and status updates Junior ▶

Що це: Test execution report описує один або кілька конкретних прогонів: які тести запускались, на якій збірці і середовищі, який статус кожного, скільки тривало, де артефакти падінь. Його здебільшого генерує інструмент (JUnit XML, Allure, звіт Playwright, TestRail run). Test summary report — людський висновок за весь етап, що спирається на багато таких прогонів.

Навіщо і коли: Execution report потрібен одразу після прогону тим, хто розбирає падіння: розробнику і автоматизатору. Summary потрібен тим, хто приймає рішення, і пишеться рідше. Змішування двох форматів дає або перевантажений summary, або execution report, з якого незрозуміло, що робити.

Приклад з проєкту: Нічний регрес на 1 180 API-тестів видавав execution report в Allure. Зранку черговий QA переглядав 9 падінь, 6 з них виявились однією причиною (зламаний тестовий акаунт), а 3 — новими дефектами. У тижневий summary потрапили вже не 9 червоних рядків, а «3 нові дефекти, одна проблема тестових даних виправлена».

Підводні камені: Пересилати менеджеру посилання на Allure замість висновку. Рахувати в summary статуси останнього прогону, а не тренд за спринт.

Уточнююче питання: «Скільки падінь у звіті — це скільки дефектів?» Ні: спершу групуємо падіння за причиною, і лише частина з них — дефекти продукту.

What it is: A test execution report describes one or more specific runs: which tests ran, on which build and environment, the status of each one, how long it took and where the failure artefacts are. It is mostly generated by a tool (JUnit XML, Allure, the Playwright report, a TestRail run). A test summary report is a human conclusion for the whole phase, based on many such runs.

Why and when: The execution report is needed right after a run by whoever investigates failures: developers and automation engineers. The summary is for decision makers and is written less often. Mixing the two gives you either a bloated summary or an execution report that does not say what to do.

From a project: A nightly regression of 1,180 API tests produced an execution report in Allure. In the morning the on-duty QA went through 9 failures: 6 turned out to share one cause (a broken test account) and 3 were new defects. The weekly summary then said not «9 red rows» but «3 new defects, one test data problem fixed».

Pitfalls: Sending a manager an Allure link instead of a conclusion. Counting the statuses of the last run in the summary instead of the trend across the sprint.

Follow-up question: «Does the number of failures equal the number of defects?» No: first group failures by cause, and only some of them are product defects.

Execution report після нічного прогону
Execution report after a nightly run
Прогін: api-regression #2317 · build 5.12.0-481 · env qa-2 · 2026-09-22 02:10 UTC
Тривалість: 18 хв 42 с · тестів 1180 · passed 1171 · failed 9 · skipped 0

Падіння, згруповані за причиною:
  6  AssertionError 401 Unauthorized   -> тестовий акаунт qa_billing_07 заблоковано (проблема даних)
  2  expected 200, got 500 /orders/refund -> новий дефект ORD-3302
  1  timeout 30 s /reports/export       -> новий дефект RPT-118, відтворюється 3/3

Артефакти: allure/2317/index.html · logs s3://ci-logs/2317/
Run: api-regression #2317 · build 5.12.0-481 · env qa-2 · 2026-09-22 02:10 UTC
Duration: 18 min 42 s · tests 1180 · passed 1171 · failed 9 · skipped 0

Failures grouped by cause:
  6  AssertionError 401 Unauthorized   -> test account qa_billing_07 locked (data problem)
  2  expected 200, got 500 /orders/refund -> new defect ORD-3302
  1  timeout 30 s /reports/export       -> new defect RPT-118, reproduces 3/3

Artefacts: allure/2317/index.html · logs s3://ci-logs/2317/
🔧 Що в артефакті: Шапка фіксує прогін, збірку і середовище, а падіння згруповані за причиною, а не перелічені поштучно. Так 9 червоних тестів перетворюються на одну проблему даних і два дефекти.
🔧 What the artefact shows: The header pins down the run, build and environment, and failures are grouped by cause instead of listed one by one. That turns 9 red tests into one data problem and two defects.
🗣 Як читати вголос Для мене execution report — це робочий документ для тих, хто лагодить: що запускали, на якій збірці, що впало і де trace. Summary — це вже висновок для тих, хто вирішує. Перш ніж щось переносити з execution report у summary, я групую падіння за причиною. Дев'ять червоних тестів можуть виявитися одним заблокованим тестовим акаунтом і двома реальними багами, і менеджеру треба знати саме це.
🗣 How to say it out loud To me an execution report is a working document for the people who fix things: what ran, on which build, what failed and where the trace is. The summary is a conclusion for the people who decide. Before moving anything from the execution report into the summary I group failures by cause. Nine red tests can turn out to be one locked test account and two real bugs, and that is what a manager needs to hear.
4 Як звітувати про прогрес тестування під час спринту? How do you report testing progress during a sprint?
Звіти про тестування і статусTest reports and status updates Junior ▶

Що це: Прогрес-звіт — коротке регулярне повідомлення: скільки історій уже перевірено, що в роботі, що заблоковано і чи встигаємо до кінця спринту. Зазвичай це 5–7 рядків у командному каналі або на стендапі, а не окремий документ.

Навіщо і коли: Щоб проблеми було видно на третій день спринту, а не в останній. Якщо тестове середовище лежить два дні, тімлід має дізнатися про це, поки ще можна перепланувати. Найкраще працює фіксований час і фіксований формат, які команда звикає читати.

Приклад з проєкту: У спринті з 11 історіями на шостий день було перевірено 4, три чекали на деплой у QA-середовище, а одна була заблокована відсутнім ключем до платіжного sandbox. Повідомлення в каналі з конкретним власником блокера («потрібен ключ, @devops, до завтра») дало ключ за дві години.

Підводні камені: Писати відсотки виконаних тест-кейсів без контексту: «60% виконано» нічого не каже, якщо решта 40% — найризиковіші сценарії. Мовчати про блокери, сподіваючись, що вони розв'яжуться самі. Повторювати те саме щодня без змін — тоді повідомлення перестають читати.

Уточнююче питання: «Що робите, якщо бачите, що не встигаєте?» Кажу про це одразу, пропоную, що тестувати в першу чергу за ризиком, і що перенести.

What it is: A progress report is a short regular message: how many stories are verified, what is in progress, what is blocked and whether we will finish by the end of the sprint. Usually it is 5 to 7 lines in the team channel or at stand-up, not a separate document.

Why and when: So that problems are visible on day three of the sprint, not on the last day. If the test environment is down for two days, the team lead needs to know while there is still time to re-plan. A fixed time and a fixed format that the team gets used to reading work best.

From a project: In a sprint with 11 stories, by day six 4 were verified, three were waiting for deployment to the QA environment and one was blocked by a missing payment sandbox key. A channel message naming the blocker's owner («need the key, @devops, by tomorrow») got the key within two hours.

Pitfalls: Reporting the percentage of executed test cases without context: «60% done» says nothing if the remaining 40% are the riskiest scenarios. Staying quiet about blockers in the hope they resolve themselves. Posting the same text every day with no change, so people stop reading it.

Follow-up question: «What do you do when you see you will not make it?» I say so straight away, propose what to test first by risk and what to move out.

Щоденний статус у каналі команди
Daily status in the team channel
QA статус · Спринт 42 · день 6 з 10
Перевірено: 4/11 історій (PAY-310, PAY-312, PAY-315, PAY-318)
В роботі: 3 — PAY-311, PAY-316 чекають деплой на qa-1 (очікуємо сьогодні 14:00)
Заблоковано: PAY-313 — немає ключа до sandbox платіжного провайдера, @devops, потрібен до завтра
Нові дефекти за добу: 2 (1 Major PAY-331, 1 Minor PAY-332)
Ризик: якщо PAY-313 не розблокуємо до четверга, регрес платежів не встигне до демо
QA status · Sprint 42 · day 6 of 10
Verified: 4/11 stories (PAY-310, PAY-312, PAY-315, PAY-318)
In progress: 3 — PAY-311, PAY-316 waiting for deploy to qa-1 (expected today 14:00)
Blocked: PAY-313 — no key for the payment provider sandbox, @devops, needed by tomorrow
New defects in last 24 h: 2 (1 Major PAY-331, 1 Minor PAY-332)
Risk: if PAY-313 is not unblocked by Thursday, payments regression will miss the demo
🔧 Що в артефакті: Кожен блокер має власника і строк, а останній рядок перекладає стан у наслідок для спринту. Історії названі ключами, тож тімлід може відкрити будь-яку одним кліком.
🔧 What the artefact shows: Every blocker has an owner and a deadline, and the last line turns the state into a consequence for the sprint. Stories are named by key so the lead can open any of them with one click.
🗣 Як читати вголос Під час спринту я пишу короткий статус щодня в один і той самий час. Там завжди чотири речі: що перевірено, що в роботі, що заблоковано і хто може розблокувати, і чи є ризик для цілей спринту. Відсотки кейсів самі по собі я не пишу, бо шістдесят відсотків можуть бути найпростішими сценаріями. Якщо бачу, що не встигаємо, кажу про це на третій-четвертий день і пропоную пріоритети за ризиком.
🗣 How to say it out loud During a sprint I post a short status every day at the same time. It always has four things: what is verified, what is in progress, what is blocked and who can unblock it, and whether the sprint goal is at risk. I do not post bare test-case percentages, because sixty percent can easily be the easy scenarios. If I see we will not make it, I say so on day three or four and propose priorities based on risk.
5 Що означає статус Blocked у тест-кейсі і як його показувати у звіті? What does the Blocked status mean for a test case, and how do you show it in a report?
Звіти про тестування і статусTest reports and status updates Junior ▶

Що це: Blocked означає, що тест неможливо виконати через зовнішню причину: не піднялося середовище, немає тестових даних чи доступу, або попередній дефект не дає дійти до перевірки. На відміну від Failed, заблокований тест нічого не каже про якість перевірюваної функції — результату просто немає.

Навіщо і коли: Blocked окремо від Failed і Not Run потрібен, щоб звіт не брехав. Якщо позначити заблокований кейс як Failed, дефектів здається більше; якщо як Passed «бо там точно все добре» — з'являється хибна впевненість. У звіті блоковані кейси завжди йдуть із причиною і посиланням на блокер.

Приклад з проєкту: 38 кейсів повернень коштів були заблоковані одним дефектом REF-90: кнопка «Повернути» не з'являлась для замовлень старших за 24 години. У звіті це був один рядок «38 blocked by REF-90», і саме цей дефект отримав найвищий пріоритет, бо закривав доступ до цілої функціональності.

Підводні камені: Вважати Blocked «майже пройденими» у pass rate. Не перевіряти блоковані кейси після розблокування: вони так і висять у звіті місяцями.

Уточнююче питання: «Чим Blocked відрізняється від Skipped в автотестах?» Skipped — свідоме рішення не запускати тест (анотація чи умова), Blocked — неможливість виконати його в цьому прогоні.

What it is: Blocked means the test cannot be executed for an external reason: the environment is not up, test data or access is missing, or an earlier defect prevents you from reaching the check. Unlike Failed, a blocked test says nothing about the quality of the feature under test; there is simply no result.

Why and when: Keeping Blocked separate from Failed and Not Run stops the report from lying. Mark a blocked case as Failed and there seem to be more defects; mark it Passed «because it is surely fine» and you create false confidence. In a report, blocked cases always come with a reason and a link to the blocker.

From a project: 38 refund test cases were blocked by one defect, REF-90: the Refund button did not appear for orders older than 24 hours. The report showed it as a single line «38 blocked by REF-90», and that defect got top priority because it cut off access to an entire feature.

Pitfalls: Treating Blocked as «almost passed» in the pass rate. Forgetting to rerun blocked cases once unblocked, so they hang in the report for months.

Follow-up question: «How is Blocked different from Skipped in automated tests?» Skipped is a deliberate decision not to run a test (an annotation or a condition); Blocked is being unable to run it in this cycle.

Блоковані кейси згруповані за блокером
Blocked cases grouped by blocker
Статус виконання · Refunds · build 2.8.1
Passed 64 · Failed 5 · Blocked 41 · Not run 0 · Разом 110

Blocked за причиною:
  38  REF-90   кнопка «Повернути» відсутня для замовлень старших за 24 год (дефект, Critical)
   3  ENV-12   sandbox банку повертає 503 з 09:00 (середовище, @devops)

Pass rate виконаних: 64 / (64 + 5) = 92.8%
Виконано від плану:  (64 + 5) / 110 = 62.7%  <- це і є реальна картина
Execution status · Refunds · build 2.8.1
Passed 64 · Failed 5 · Blocked 41 · Not run 0 · Total 110

Blocked by cause:
  38  REF-90   Refund button missing for orders older than 24 h (defect, Critical)
   3  ENV-12   bank sandbox returns 503 since 09:00 (environment, @devops)

Pass rate of executed: 64 / (64 + 5) = 92.8%
Executed of planned:   (64 + 5) / 110 = 62.7%  <- this is the real picture
🔧 Що в артефакті: Блоковані кейси згруповані за причиною, тож видно, що один дефект закриває 38 перевірок. Дві останні формули показують, чому 92.8% pass rate без частки виконаних вводить в оману.
🔧 What the artefact shows: Blocked cases are grouped by cause, so it is clear one defect hides 38 checks. The last two formulas show why a 92.8% pass rate without the executed share is misleading.
🗣 Як читати вголос Blocked для мене означає, що результату немає взагалі: тест не можна виконати через середовище, дані чи інший дефект. Тому я ніколи не змішую його з Failed чи Passed. У звіті групую блоковані кейси за блокером — часто видно, що один баг закриває кілька десятків перевірок, і тоді це найважливіший баг тижня. І поруч із pass rate завжди показую, яку частину плану ми взагалі виконали.
🗣 How to say it out loud To me Blocked means there is no result at all: the test cannot run because of the environment, data or another defect. So I never merge it with Failed or Passed. In the report I group blocked cases by blocker, and often you see one bug hiding a few dozen checks, which makes it the most important bug of the week. Next to the pass rate I always show how much of the plan was actually executed.
6 Навіщо писати QA-звіт після спринту, якщо в Jira і так усе є? Why write a QA report after a sprint if everything is already in Jira?
Звіти про тестування і статусTest reports and status updates Junior ▶

Що це: Спринтовий QA-звіт — коротке резюме на пів сторінки: що з запланованого перевірено, які дефекти знайдено і де, що перенесено, які тенденції змінилися порівняно з минулим спринтом і що команда змінює далі. Jira зберігає факти поштучно, а звіт показує картину і висновок.

Навіщо і коли: Його читають на ретроспективі і на sprint review, а також люди поза командою: продакт, суміжні команди, керівник напряму. Без звіту кожен сам будує фільтри і робить свої, часто різні висновки. Звіт також фіксує рішення «прийняли ризик і випустили з відомою проблемою» — у тікетах цього не видно.

Приклад з проєкту: Три спринти поспіль у звітах було видно, що більшість дефектів знаходили в модулі імпорту CSV, і майже всі — на файлах з кирилицею чи порожніми рядками. На ретро команда вирішила додати набір «незручних» файлів у контрактні тести, і в наступному спринті дефектів імпорту стало 2 замість 9.

Підводні камені: Копіювати в звіт список тікетів без аналізу. Писати звіт, який ніхто не обговорює: якщо на ретро його не відкривають, формат треба змінити.

Уточнююче питання: «Що з цього звіту обговорюють на ретроспективі?» Тенденції і причини, а не список тікетів.

What it is: A sprint QA report is a half-page summary: what of the plan was verified, which defects were found and where, what was carried over, which trends changed compared with the previous sprint and what the team changes next. Jira stores facts one by one, while the report shows the picture and the conclusion.

Why and when: It is read at the retrospective and the sprint review, and by people outside the team: the product owner, neighbouring teams, the head of the area. Without a report everyone builds their own filters and draws their own, often different, conclusions. The report also records decisions like «we accepted the risk and shipped with a known problem», which tickets do not show.

From a project: Three sprints in a row the reports showed most defects came from the CSV import module, nearly all on files with Cyrillic text or empty lines. At the retro the team decided to add a set of «awkward» files to the contract tests, and in the next sprint import defects dropped from 9 to 2.

Pitfalls: Pasting the ticket list into the report without analysis. Writing a report nobody discusses: if it is not opened at the retro, change the format.

Follow-up question: «What from this report gets discussed at the retrospective?» Trends and causes, not the ticket list.

Спринтовий QA-звіт на пів сторінки
A half-page sprint QA report
QA-звіт · Спринт 37 (2–13 вересня)
Перевірено: 14 з 16 історій; IMP-221 і IMP-224 перенесено (дизайн не затверджено)
Дефекти: знайдено 17 (у спринті 15, після релізу 2) · закрито 19 · відкрито на кінець 6
Де знаходимо: імпорт CSV 9, звіти 4, профіль 2, інше 2
Тенденція: імпорт CSV — третій спринт поспіль лідер за дефектами (9, 8, 9)
Прийнятий ризик: реліз 7.3 вийшов з IMP-230 (повільний імпорт файлів понад 50 МБ)
Пропозиція на ретро: набір «незручних» CSV (кирилиця, BOM, порожні рядки) у контрактні тести
QA report · Sprint 37 (2–13 September)
Verified: 14 of 16 stories; IMP-221 and IMP-224 carried over (design not approved)
Defects: found 17 (15 in sprint, 2 after release) · closed 19 · open at end 6
Where we find them: CSV import 9, reports 4, profile 2, other 2
Trend: CSV import is the top defect source for the third sprint in a row (9, 8, 9)
Accepted risk: release 7.3 shipped with IMP-230 (slow import of files over 50 MB)
Proposal for retro: a set of «awkward» CSVs (Cyrillic, BOM, empty lines) in contract tests
🔧 Що в артефакті: Звіт розділяє дефекти, знайдені в спринті і після релізу, показує тренд за три спринти і закінчується конкретною пропозицією. Рядок про прийнятий ризик фіксує рішення, якого немає в жодному тікеті.
🔧 What the artefact shows: The report separates defects found in the sprint from those found after release, shows a three-sprint trend and ends with a concrete proposal. The accepted-risk line records a decision that exists in no ticket.
🗣 Як читати вголос Jira відповідає на питання «що сталося з тікетом», а спринтовий звіт — «що ми з цього дізналися». Я пишу його на пів сторінки: що перевірили, що перенесли, де знаходимо дефекти і як це змінилося за кілька спринтів. Найкорисніший рядок зазвичай останній — що пропонуємо змінити. Одного разу саме так ми побачили, що три спринти поспіль ламається імпорт файлів, і закрили це набором тестових файлів.
🗣 How to say it out loud Jira answers «what happened to this ticket», while the sprint report answers «what did we learn from it». I keep it to half a page: what we verified, what moved to the next sprint, where defects come from and how that changed over a few sprints. The most useful line is usually the last one, what we propose to change. That is exactly how we once noticed file import breaking three sprints in a row and fixed it with a set of test files.
7 Що таке test incident report і коли його потрібно писати? What is a test incident report, and when do you need to write one?
Звіти про тестування і статусTest reports and status updates Middle ▶

Що це: Test incident report — запис про будь-яку подію під час тестування, яка потребує розбору: результат не збігся з очікуваним, тест поводився дивно, середовище впало посеред прогону. Термін походить з IEEE 829, у ISO/IEC/IEEE 29119-3 це incident report. Ключова різниця з баг-репортом: на момент запису ще невідомо, чи це дефект продукту, помилка в тесті, проблема даних чи середовища.

Навіщо і коли: Його пишуть у регульованих і контрактних проєктах (медицина, фінанси, державні замовлення), де кожна аномалія має бути зафіксована і закрита з рішенням. У звичайній продуктовій команді ту саму роль часто грає тікет типу Test Incident або коментар до тест-рану, але суть та сама: факт, вплив, аналіз, рішення.

Приклад з проєкту: Під час приймального тестування системи для лабораторії 12 результатів аналізів показали одиниці ммоль/л замість мг/дл. Ми оформили incident: після аналізу з'ясувалося, що тестові дані завантажили з європейського профілю. Інцидент закрили як «помилка тестових даних», дефект не створювали, але зафіксували перевірку профілю в чек-листі підготовки.

Підводні камені: Одразу реєструвати кожну аномалію як баг — це засмічує дефектні метрики хибними дефектами. Або навпаки, не фіксувати «дивне» взагалі, і через місяць ніхто не пам'ятає, чому тест-ран позначили повторним.

Уточнююче питання: «Що стається з інцидентом після аналізу?» Він закривається з класифікацією: дефект (створюємо баг і лінкуємо), помилка тесту, дані, середовище або не відтворюється.

What it is: A test incident report records any event during testing that needs investigation: a result did not match the expectation, a test behaved oddly, the environment went down in the middle of a run. The term comes from IEEE 829; in ISO/IEC/IEEE 29119-3 it is the incident report. The key difference from a bug report is that at the time of writing nobody knows yet whether it is a product defect, a test mistake, or a data or environment problem.

Why and when: It is required in regulated and contract projects (medical, finance, government), where every anomaly must be recorded and closed with a decision. In an ordinary product team the same role is often played by a Test Incident ticket type or a comment on the test run, but the substance is identical: fact, impact, analysis, decision.

From a project: During acceptance testing of a laboratory system, 12 lab results showed mmol/L instead of mg/dL. We raised an incident, and analysis showed the test data had been loaded from the European profile. The incident was closed as «test data error», no defect was created, and a profile check was added to the preparation checklist.

Pitfalls: Filing every anomaly straight away as a bug, which fills defect metrics with false defects. Or the opposite: not recording «odd» things at all, so a month later nobody remembers why a test run was marked as repeated.

Follow-up question: «What happens to an incident after analysis?» It is closed with a classification: defect (a bug is created and linked), test error, data, environment, or cannot reproduce.

Test incident report з рішенням
Test incident report with its resolution
TEST INCIDENT TI-0147
Дата/час: 2026-09-10 11:42 · Тестувальник: О. Коваль · Етап: приймальне тестування
Об'єкт: LabResults 4.2.0-rc1, середовище uat-lab, тест-кейс LR-TC-088
Опис: 12 результатів глюкози показано в ммоль/л, очікувались мг/дл (US-профіль)
Вплив: блокує 14 кейсів звітності по глюкозі; ризик для пацієнта, якщо це дефект
Аналіз: дані імпортовано з профілю EU (unit_system=SI); у US-профілі одиниці коректні
Класифікація: помилка тестових даних (не дефект продукту)
Рішення: дані перезавантажено, LR-TC-088 повторено — Passed
Запобігання: пункт «перевірити unit_system» додано в чек-лист підготовки UAT
Статус: закрито 2026-09-10 16:05 · підтвердив тест-лід
TEST INCIDENT TI-0147
Date/time: 2026-09-10 11:42 · Tester: O. Koval · Phase: acceptance testing
Item: LabResults 4.2.0-rc1, environment uat-lab, test case LR-TC-088
Description: 12 glucose results shown in mmol/L, expected mg/dL (US profile)
Impact: blocks 14 glucose reporting cases; patient risk if this is a defect
Analysis: data imported from the EU profile (unit_system=SI); units are correct in US profile
Classification: test data error (not a product defect)
Resolution: data reloaded, LR-TC-088 rerun — Passed
Prevention: «check unit_system» added to the UAT preparation checklist
Status: closed 2026-09-10 16:05 · confirmed by test lead
🔧 Що в артефакті: Інцидент має поля, яких немає в баг-репорті: аналіз, класифікацію причини і рішення. Так видно, що аномалію розібрали і свідомо не порахували як дефект продукту.
🔧 What the artefact shows: The incident has fields a bug report lacks: analysis, cause classification and resolution. That shows the anomaly was investigated and consciously not counted as a product defect.
🗣 Як читати вголос Test incident report я пояснюю як «запис про дивне до того, як ми знаємо, що це». Під час тестування не все, що не збіглося з очікуванням, — баг: буває зламаний тест, погані дані чи середовище. Інцидент фіксує факт і вплив, а після аналізу закривається з класифікацією. Якщо це дефект — створюю баг і лінкую. На регульованих проєктах без такого сліду просто не пройдеш аудит.
🗣 How to say it out loud I describe a test incident report as «a record of something odd before we know what it is». Not everything that differs from the expectation during testing is a bug: it can be a broken test, bad data or the environment. The incident captures the fact and the impact, and after analysis it gets closed with a classification. If it turns out to be a defect, I raise a bug and link it. On regulated projects you will not pass an audit without that trail.
8 У звіті 98% тестів пройшли, але відкриті два critical-дефекти в оплаті. Що має бути у висновку? The report shows 98% passed, but two critical payment defects are open. What should the conclusion say?
Звіти про тестування і статусTest reports and status updates Middle ▶

Що це: Це класична пастка: pass rate відображає частку зелених тестів, а не частку ризику. Два падіння в оплаті важать більше, ніж сотня зелених перевірок профілю. Висновок звіту має починатися не з 98%, а з того, що критичний бізнес-потік зламаний і реліз у поточному вигляді не рекомендований.

Навіщо і коли: Керівник, який бачить «98% passed», інтуїтивно читає «майже готово». Тому висновок формулюється через наслідок для бізнесу: хто постраждає, як часто, чи є обхід, що потрібно для зміни рекомендації. Цифри лишаються, але як доказ.

Приклад з проєкту: 588 з 600 тестів зелені, але PAY-501 (подвійне списання при повторному натисканні «Сплатити» на повільній мережі) і PAY-507 (3-D Secure не повертає користувача в застосунок на Android 14). Висновок: NO-GO до виправлення PAY-501; PAY-507 можна випускати, тільки якщо на Android 14 вимкнути нову форму оплати прапорцем. Після фіксу потрібна перевірка оплати на реальних картках у sandbox, приблизно пів дня.

Підводні камені: Ховати криті дефекти в таблицю на третій сторінці. Писати «на розсуд менеджменту» без рекомендації. Або, навпаки, оголошувати реліз заблокованим без варіантів — фіча-прапорець, обхід, частковий реліз.

Уточнююче питання: «А якщо бізнес усе одно вирішить випускати?» Фіксую рішення, власника ризику і план моніторингу подвійних списань після релізу.

What it is: This is the classic trap: pass rate reflects the share of green tests, not the share of risk. Two failures in payments outweigh a hundred green profile checks. The conclusion must not start with 98%; it must start with the fact that a critical business flow is broken and the release is not recommended as it stands.

Why and when: A manager who sees «98% passed» intuitively reads «nearly done». So the conclusion is phrased in terms of business consequence: who is affected, how often, whether there is a workaround, and what would change the recommendation. The numbers stay, but as evidence.

From a project: 588 of 600 tests were green, but PAY-501 (double charge when the Pay button is pressed again on a slow network) and PAY-507 (3-D Secure does not return the user to the app on Android 14) were open. Conclusion: NO-GO until PAY-501 is fixed; PAY-507 can ship only if the new payment form is switched off by flag on Android 14. After the fix, a payment check with real cards in the sandbox is needed, about half a day.

Pitfalls: Burying critical defects in a table on page three. Writing «at management's discretion» without a recommendation. Or the opposite: declaring the release blocked with no options such as a feature flag, a workaround or a partial release.

Follow-up question: «What if the business decides to ship anyway?» I record the decision, the risk owner and a plan to monitor double charges after release.

Висновок звіту: ризик замість відсотка
Report conclusion: risk instead of a percentage
ВИСНОВОК · Mobile 6.1.0 build 611
Рекомендація: NO-GO до виправлення PAY-501.

Чому: 2 відкриті Critical у платежах.
  PAY-501  повторне натискання «Сплатити» при повільній мережі -> подвійне списання.
           Відтворюється 4/5 на 3G-профілі. Обходу для користувача немає.
  PAY-507  3-D Secure не повертає в застосунок на Android 14 (~31% Android-аудиторії).
           Можна випускати, якщо flag new_checkout_form=off для Android 14.

Що змінить рекомендацію на GO: фікс PAY-501 + 0.5 дня перевірки оплати в sandbox.
Для довідки: виконано 600/600, пройдено 588 (98%); решта 10 падінь — Minor у профілі.
CONCLUSION · Mobile 6.1.0 build 611
Recommendation: NO-GO until PAY-501 is fixed.

Why: 2 open Critical defects in payments.
  PAY-501  pressing Pay again on a slow network -> double charge.
           Reproduces 4/5 on a 3G profile. No workaround for the user.
  PAY-507  3-D Secure does not return to the app on Android 14 (~31% of Android users).
           Can ship if flag new_checkout_form=off for Android 14.

What changes the recommendation to GO: fix for PAY-501 + 0.5 day of sandbox payment checks.
For reference: executed 600/600, passed 588 (98%); the other 10 failures are Minor in profile.
🔧 Що в артефакті: Рекомендація стоїть першою, кожен дефект описаний через наслідок і охоплення, а 98% з'являються лише в останньому рядку як довідка. Рядок «що змінить рекомендацію» дає команді конкретний шлях до GO.
🔧 What the artefact shows: The recommendation comes first, each defect is described by consequence and reach, and 98% appears only on the last line as reference. The «what changes the recommendation» line gives the team a concrete path to GO.
🗣 Як читати вголос Я б не починав висновок з дев'яноста восьми відсотків, бо менеджер прочитає це як «майже готово». Першим реченням пишу рекомендацію: не випускати, доки не виправлено подвійне списання, бо обходу для користувача немає. Для другого дефекту пропоную вихід — вимкнути нову форму оплати прапорцем. І обов'язково пишу, що саме змінить рекомендацію на GO і скільки часу це займе.
🗣 How to say it out loud I would not open the conclusion with ninety-eight percent, because a manager reads that as «almost done». My first sentence is the recommendation: do not ship until the double charge is fixed, because users have no workaround. For the second defect I offer a way out, switching the new payment form off with a flag. And I always state what would change the recommendation to GO and how long it would take.
9 Як звітувати результати навантажувального тестування і performance-дефекти? How do you report performance test results and performance defects?
Звіти про тестування і статусTest reports and status updates Middle ▶

Що це: Звіт з продуктивності порівнює виміряні значення з заздалегідь погодженими цілями (SLO або нефункціональними вимогами): перцентилі часу відповіді (p95, p99), пропускну здатність, частку помилок і ресурси під певним навантаженням. Performance-дефект — це порушення такої цілі, описане з профілем навантаження, а не фраза «сторінка повільна».

Навіщо і коли: Без цілі і профілю цифри не можна інтерпретувати: 900 мс — це добре для генерації PDF і погано для автодоповнення. Звіт потрібен перед релізами з інфраструктурними змінами, перед сезонними піками і після оптимізацій, щоб показати ефект порівняно з базовою лінією.

Приклад з проєкту: Перед Black Friday ми ганяли сценарій «пошук → картка → кошик» на 1 200 віртуальних користувачах протягом 30 хвилин. Пошук тримав p95 410 мс при цілі 500 мс, а додавання в кошик — 1 850 мс при цілі 800 мс і 2.3% помилок 504. Дефект описали з профілем, збіркою, графіком і тим, що деградація починається з ~800 користувачів; причиною виявився пул з'єднань до Redis на 50.

Підводні камені: Звітувати середнє замість перцентилів — воно ховає хвіст повільних запитів. Порівнювати прогони з різними даними чи середовищем. Не вказувати, на якому навантаженні почалася деградація.

Уточнююче питання: «Чому p95, а не середнє?» Бо середнє 300 мс може означати, що кожен двадцятий користувач чекає 3 секунди.

What it is: A performance report compares measured values with targets agreed in advance (SLOs or non-functional requirements): response time percentiles (p95, p99), throughput, error rate and resource usage under a given load. A performance defect is a breach of such a target described together with the load profile, not the phrase «the page is slow».

Why and when: Without a target and a profile the numbers cannot be interpreted: 900 ms is fine for generating a PDF and bad for autocomplete. The report is needed before releases with infrastructure changes, before seasonal peaks and after optimisations, to show the effect against a baseline.

From a project: Before Black Friday we ran a «search → product page → cart» scenario with 1,200 virtual users for 30 minutes. Search held p95 at 410 ms against a 500 ms target, while add-to-cart hit 1,850 ms against 800 ms with 2.3% of 504 errors. The defect was filed with the profile, build, a chart and the fact that degradation starts at about 800 users; the cause was a Redis connection pool of 50.

Pitfalls: Reporting the average instead of percentiles, which hides the tail of slow requests. Comparing runs with different data or environments. Not stating the load at which degradation began.

Follow-up question: «Why p95 and not the average?» Because an average of 300 ms can mean one user in twenty waits 3 seconds.

Звіт навантажувального тесту проти SLO
Load test report against SLOs
LOAD TEST · shop-api 9.4.0 · env perf-1 (копія прод-конфігурації, 3 поди) · 2026-09-15
Профіль: 1200 VU, розгін 5 хв, утримання 30 хв, сценарій search -> product -> add_to_cart

Операція       p50     p95      p99      Помилки  Ціль p95  Статус
search         120 ms  410 ms   690 ms   0.0%     500 ms    OK
product_page    95 ms  280 ms   520 ms   0.0%     400 ms    OK
add_to_cart    640 ms  1850 ms  3900 ms  2.3%     800 ms    ПОРУШЕНО

Деградація add_to_cart починається з ~800 VU (див. графік latency vs VU)
Дефект: PERF-77 — пул з'єднань Redis = 50, під навантаженням черга до 1.2 с
Базова лінія 9.3.0: add_to_cart p95 = 720 ms при 1200 VU -> регресія
LOAD TEST · shop-api 9.4.0 · env perf-1 (copy of prod config, 3 pods) · 2026-09-15
Profile: 1200 VU, 5 min ramp-up, 30 min hold, scenario search -> product -> add_to_cart

Operation      p50     p95      p99      Errors   Target p95  Status
search         120 ms  410 ms   690 ms   0.0%     500 ms      OK
product_page    95 ms  280 ms   520 ms   0.0%     400 ms      OK
add_to_cart    640 ms  1850 ms  3900 ms  2.3%     800 ms      BREACHED

add_to_cart degradation starts at ~800 VU (see latency vs VU chart)
Defect: PERF-77 — Redis connection pool = 50, queue up to 1.2 s under load
Baseline 9.3.0: add_to_cart p95 = 720 ms at 1200 VU -> regression
🔧 Що в артефакті: Кожна операція має перцентилі, частку помилок і ціль в одному рядку, тож порушення видно без пояснень. Порівняння з базовою лінією попередньої версії доводить, що це регресія, а не особливість середовища.
🔧 What the artefact shows: Each operation shows percentiles, error rate and target on one row, so the breach is obvious. The comparison with the previous version's baseline proves it is a regression and not a quirk of the environment.
🗣 Як читати вголос Про продуктивність я звітую тільки відносно цілі. Кажу, яким був профіль навантаження, на якій збірці і середовищі, і показую p95 та p99 разом із часткою помилок. Середнє не використовую, бо воно ховає повільний хвіст. Performance-дефект у мене завжди містить точку, з якої починається деградація, і порівняння з попередньою версією — тоді розробникам зрозуміло, що шукати, а менеджерам — наскільки це серйозно.
🗣 How to say it out loud I report performance only against a target. I state the load profile, the build and the environment, and I show p95 and p99 together with the error rate. I avoid the average because it hides the slow tail. A performance defect from me always includes the load level where degradation begins and a comparison with the previous version, so developers know what to look for and managers see how serious it is.
▶ 2 7
10 Що таке JUnit XML і чому саме цей формат розуміють усі CI-системи? What is JUnit XML, and why is it the common denominator of CI test reports?
Інструменти й артефакти звітностіReporting tools and artefacts Junior ▶

Що це: JUnit XML — текстовий формат результатів тестів, який історично з'явився разом із JUnit і Ant, а тепер його вміє писати майже кожен раннер: Playwright (reporter: 'junit'), pytest (--junitxml), Maven Surefire, Jest через jest-junit. Кореневий <testsuites> містить набори <testsuite> з лічильниками tests, failures, errors, skipped і time, а кожен <testcase> може мати дочірній <failure>, <error> або <skipped>.

Навіщо і коли: Це спільна мова між раннером і платформою: GitLab показує вкладку Tests у merge request, Jenkins малює тренд через плагін JUnit, Azure DevOps і GitHub-екшени для звітів теж читають саме його. Коли в команді три стеки (Java API, Python-сервіси, Playwright UI), один формат дає одну зведену картину без трьох різних порталів.

Приклад з проєкту: У монорепо ми збирали 4 файли junit-*.xml з різних джобів, і GitLab показував у MR, що з 1 212 тестів упав один новий, а два падали й до цієї гілки. Ревʼюер бачив це без жодного відкриття логів.

Підводні камені: Різниця між failure (не виконалась перевірка) і error (тест зламався до перевірки) у різних раннерах трактується по-різному. У форматі немає поняття «flaky» і ретраїв: залежно від раннера перезапущений тест може зʼявитись двічі або лише з останнім результатом. І XML не несе скриншотів — лише текст і шляхи.

Уточнююче питання: «Чим відрізняється failure від error у звіті?» — failure означає, що продукт повів себе не так, як очікувалось; error частіше вказує на проблему тесту або оточення, і таке падіння спершу розбирає автоматизатор.

What it is: JUnit XML is a plain-text format for test results that grew out of JUnit and Ant, and today nearly every runner can write it: Playwright (reporter: 'junit'), pytest (--junitxml), Maven Surefire, Jest via jest-junit. A root <testsuites> holds <testsuite> elements with tests, failures, errors, skipped and time counters, and each <testcase> may carry a <failure>, <error> or <skipped> child.

Why and when: It is the shared language between a runner and a platform: GitLab shows a Tests tab on the merge request, Jenkins draws a trend with the JUnit plugin, and Azure DevOps and GitHub reporting actions read the same file. When a team runs three stacks (Java API, Python services, Playwright UI), one format gives one combined picture instead of three portals.

From a project: In a monorepo we collected four junit-*.xml files from separate jobs, and GitLab showed on the MR that out of 1,212 tests one new test had failed while two were already failing before the branch. The reviewer saw that without opening a single log.

Pitfalls: Runners disagree on failure (an assertion did not hold) versus error (the test broke before reaching the check). The format has no notion of flaky tests or retries, so depending on the runner a rerun test may appear twice or only with its last outcome. And the XML carries no screenshots, only text and paths.

Follow-up question: "How does failure differ from error in the report?" A failure says the product behaved unexpectedly; an error usually points at the test or the environment, so an automation engineer looks at it first.

Фрагмент JUnit XML з Playwright
A JUnit XML fragment from Playwright
<testsuites tests="3" failures="1" errors="0" skipped="1" time="14.2">
  <testsuite name="checkout.spec.ts" tests="3" failures="1" errors="0" skipped="1" time="14.2">
    <testcase name="guest can pay by card" classname="checkout.spec.ts" time="6.1"/>
    <testcase name="promo code reduces total" classname="checkout.spec.ts" time="7.9">
      <failure message="expect(received).toHaveText(expected)" type="FAILURE">
Expected: "90.00"
Received: "100.00"
      </failure>
    </testcase>
    <testcase name="pay with Apple Pay" classname="checkout.spec.ts" time="0.2">
      <skipped/>
    </testcase>
  </testsuite>
</testsuites>
🔧 Що в артефакті: Лічильники на testsuite — те, що CI показує в заголовку звіту; вкладений failure несе повідомлення перевірки, а порожній skipped позначає пропущений тест. Сума атрибутів має збігатися з кількістю testcase, інакше графіки в CI розʼїдуться.
🔧 What the artefact shows: The counters on testsuite are what CI shows in the report header; the nested failure carries the assertion message, and an empty skipped marks a skipped test. The counters must match the number of testcase elements, otherwise CI charts drift.
💡 Простими словами: JUnit XML — це як стандартна розетка: байдуже, який прилад (раннер), якщо штекер підходить до стіни (CI).
💡 In plain words: JUnit XML is like a standard power socket: it does not matter which appliance (runner) you have as long as the plug fits the wall (CI).
🗣 Як читати вголос Я сприймаю JUnit XML як мінімальний спільний знаменник. Що б не писала команда — pytest, Playwright чи Maven, — я прошу, щоб кожна джоба віддавала цей файл, бо тоді GitLab чи Jenkins самі покажуть, що впало в конкретному merge request. Але я чесно кажу про обмеження: там немає скриншотів, немає поняття нестабільного тесту, тому для розбору падінь поруч живе Allure або trace. XML — для машини і для швидкого сигналу, а не для глибокого аналізу.
🗣 How to say it out loud I treat JUnit XML as the lowest common denominator. Whatever the team writes in — pytest, Playwright or Maven — I ask every job to emit that file, because then GitLab or Jenkins shows by itself what broke in a given merge request. I am also upfront about its limits: no screenshots, no idea of a flaky test, so for digging into failures we keep Allure or traces next to it. The XML is for the machine and the quick signal, not for deep analysis.
11 Як підключити Allure до Playwright і що дають history, categories та environment? How do you set up Allure for Playwright, and what do history, categories and environment add?
Інструменти й артефакти звітностіReporting tools and artefacts Middle ▶

Що це: allure-playwright — репортер, який під час прогону пише сирі результати (JSON і вкладення) у папку allure-results. Окремий крок — allure generate або allure serve — перетворює їх на HTML-звіт з кроками, вкладеннями, групуванням за suite, feature і severity.

Навіщо і коли: Три речі роблять Allure корисним саме для звітності, а не лише для перегляду падінь. Environment показує, на якому стенді, браузері й білді йшов прогін. Categories розкладає падіння за правилами на регулярних виразах: «продуктові дефекти», «таймаути інфраструктури», «зламані локатори». History дає тренд і позначку нестабільності тесту між прогонами.

Приклад з проєкту: Після налаштування categories з'ясувалось, що з 37 червоних тестів нічного прогону 29 — це TimeoutError на одному стенді, а реальних продуктових падінь лише 8. Ранкова розмова з командою тривала 5 хвилин замість години.

Підводні камені: History не зберігається сама: CI-джоба мусить перед генерацією скопіювати папку history з попереднього звіту в нові allure-results, інакше тренд завжди порожній. Файл categories.json теж треба класти в results до генерації. Якщо не чистити allure-results між прогонами, результати змішуються і лічильники брешуть.

Уточнююче питання: «Чому тренд в Allure порожній, хоча прогонів було двадцять?» — бо в CI не переносять history між звітами або кожен раз стартують з чистого робочого простору.

What it is: allure-playwright is a reporter that writes raw results (JSON plus attachments) into an allure-results folder during the run. A separate step, allure generate or allure serve, turns them into an HTML report with steps, attachments and grouping by suite, feature and severity.

Why and when: Three features make Allure useful for reporting rather than only for browsing failures. Environment records which stand, browser and build the run used. Categories sorts failures by regex rules into buckets such as product defects, infrastructure timeouts and broken locators. History gives the trend and marks a test as unstable across runs.

From a project: Once categories were in place, it turned out that 29 of the 37 red tests in a nightly run were a TimeoutError on one stand, and only 8 were real product failures. The morning sync with the team took five minutes instead of an hour.

Pitfalls: History does not persist on its own: the CI job has to copy the history folder from the previous report into the new allure-results before generating, or the trend stays empty. The categories.json file must also land in results before generation. If allure-results is not cleaned between runs, results mix and the counters lie.

Follow-up question: "Why is the Allure trend empty after twenty runs?" Because CI does not carry history from one report to the next, or each run starts from a clean workspace.

Репортер, categories.json і перенесення history
Reporter, categories.json and carrying history over
// playwright.config.ts
reporter: [
  ['line'],
  ['allure-playwright', {
    resultsDir: 'allure-results',
    detail: true,
    environmentInfo: { stand: 'staging-2', build: process.env.BUILD_ID },
  }],
],

// allure-results/categories.json
[
  { "name": "Інфраструктура: таймаути", "matchedStatuses": ["broken", "failed"],
    "traceRegex": ".*TimeoutError.*" },
  { "name": "Продуктові дефекти", "matchedStatuses": ["failed"],
    "messageRegex": ".*expect.*" }
]

# CI: перенести history перед генерацією
cp -r previous-report/history allure-results/history
allure generate allure-results --clean -o allure-report
// playwright.config.ts
reporter: [
  ['line'],
  ['allure-playwright', {
    resultsDir: 'allure-results',
    detail: true,
    environmentInfo: { stand: 'staging-2', build: process.env.BUILD_ID },
  }],
],

// allure-results/categories.json
[
  { "name": "Infrastructure: timeouts", "matchedStatuses": ["broken", "failed"],
    "traceRegex": ".*TimeoutError.*" },
  { "name": "Product defects", "matchedStatuses": ["failed"],
    "messageRegex": ".*expect.*" }
]

# CI: carry history over before generating
cp -r previous-report/history allure-results/history
allure generate allure-results --clean -o allure-report
🔧 Що в артефакті: environmentInfo потрапляє у віджет Environment; кожне правило categories — це назва кошика, статуси і регулярний вираз по повідомленню чи стеку. Рядок cp -r — саме той крок, без якого немає тренду.
🔧 What the artefact shows: environmentInfo feeds the Environment widget; each categories rule is a bucket name, the statuses it applies to and a regex on the message or stack trace. The cp -r line is exactly the step without which there is no trend.
🗣 Як читати вголос Коли мене питають про Allure, я одразу кажу, що сам звіт — половина справи. Реальну цінність дають три налаштування: environment, щоб не гадати, де йшов прогін, categories, щоб зранку бачити «вісім продуктових падінь і двадцять дев'ять таймаутів стенду», і history, щоб бачити тренд. І я згадую граблі, на які наступав сам: history треба явно переносити в CI, інакше тренд вічно порожній, і люди думають, що Allure просто так працює.
🗣 How to say it out loud When I get asked about Allure, I say straight away that the report itself is half the job. The value comes from three settings: environment, so nobody guesses where the run happened; categories, so in the morning I see eight product failures and twenty-nine stand timeouts; and history, so I can see a trend. I also mention the trap I fell into myself: CI has to carry history over explicitly, otherwise the trend is always empty and people assume that is just how Allure works.
12 Як скриншоти, відео і trace потрапляють у CI-звіт і не забивають сховище? How do screenshots, videos and traces get into a CI report without bloating storage?
Інструменти й артефакти звітностіReporting tools and artefacts Middle ▶

Що це: Вкладення — докази до звіту: скриншот стану сторінки, відео прогону, trace Playwright з DOM-знімками, мережею і консоллю на кожен крок. У Playwright вони вмикаються в use і мають режими, що зберігають файли лише тоді, коли вони потрібні.

Навіщо і коли: Червоний тест без артефакту змушує когось перезапускати його локально, а це 10–30 хвилин на кожне падіння. Натомість screenshot: 'only-on-failure', video: 'retain-on-failure' і trace: 'on-first-retry' дають повну картину саме для тих тестів, які впали, і майже нічого не пишуть для зелених.

Приклад з проєкту: Регресія з 900 тестів з video: 'on' залишала близько 6 ГБ артефактів за прогін, і за тиждень ми вперлися в квоту сховища GitHub. Після переходу на режими «тільки при падінні» і retention-days: 7 артефакт нічного прогону важив 150–300 МБ, а розбір падінь не погіршився: trace з першого ретраю показував усе потрібне.

Підводні камені: Без if: always() крок завантаження не виконається, бо попередній крок з тестами завершився помилкою, — і артефакти зникнуть якраз тоді, коли вони потрібні. on-first-retry не дасть trace, якщо ретраї вимкнені. Відео і trace можуть містити токени й персональні дані, тож доступ до артефактів має бути обмежений.

Уточнююче питання: «Чому trace саме на першому ретраї, а не завжди?» — так ми платимо за запис лише там, де тест уже впав один раз, і отримуємо докази саме для нестабільних або зламаних сценаріїв.

What it is: Attachments are the evidence behind a report: a screenshot of the page state, a video of the run, a Playwright trace with DOM snapshots, network and console for every step. In Playwright they are switched on under use, with modes that keep files only when they are needed.

Why and when: A red test with no artefact forces someone to rerun it locally, which costs 10 to 30 minutes per failure. With screenshot: 'only-on-failure', video: 'retain-on-failure' and trace: 'on-first-retry' you get the full picture for exactly the tests that failed and almost nothing stored for green ones.

From a project: A 900-test regression with video: 'on' left about 6 GB of artefacts per run, and within a week we hit the GitHub storage quota. After switching to failure-only modes and retention-days: 7, the nightly artefact weighed 150 to 300 MB, and failure analysis got no worse: the trace from the first retry showed everything we needed.

Pitfalls: Without if: always() the upload step does not run, because the test step before it failed, so the artefacts vanish exactly when you need them. on-first-retry produces no trace if retries are disabled. Videos and traces can contain tokens and personal data, so access to artefacts should be restricted.

Follow-up question: "Why record the trace on the first retry and not always?" That way you pay for recording only where the test has already failed once, and you get evidence precisely for broken or unstable scenarios.

Налаштування Playwright і крок GitHub Actions
Playwright settings and a GitHub Actions step
// playwright.config.ts
retries: process.env.CI ? 1 : 0,
use: {
  screenshot: 'only-on-failure',
  video: 'retain-on-failure',
  trace: 'on-first-retry',
},

# .github/workflows/e2e.yml
- name: Завантажити звіт і докази
  if: always()            # навіть якщо тести впали
  uses: actions/upload-artifact@v4
  with:
    name: playwright-report-${{ github.run_id }}
    path: |
      playwright-report/
      test-results/
    retention-days: 7     # не тримати місяцями
// playwright.config.ts
retries: process.env.CI ? 1 : 0,
use: {
  screenshot: 'only-on-failure',
  video: 'retain-on-failure',
  trace: 'on-first-retry',
},

# .github/workflows/e2e.yml
- name: Upload report and evidence
  if: always()            # even when tests failed
  uses: actions/upload-artifact@v4
  with:
    name: playwright-report-${{ github.run_id }}
    path: |
      playwright-report/
      test-results/
    retention-days: 7     # do not keep for months
🔧 Що в артефакті: retries: 1 у CI потрібен, щоб on-first-retry взагалі спрацював; test-results/ містить скриншоти, відео і trace.zip, а playwright-report/ — HTML-звіт, що на них посилається. retention-days обмежує, скільки днів GitHub зберігає артефакт.
🔧 What the artefact shows: retries: 1 in CI is what lets on-first-retry fire at all; test-results/ holds screenshots, videos and trace.zip files, while playwright-report/ is the HTML report that links to them. retention-days caps how long GitHub keeps the artefact.
🗣 Як читати вголос Моє правило просте: кожне червоне падіння в CI має приходити з доказами, а зелене — не коштувати нічого. Тому скриншот і відео я зберігаю лише для тестів, що впали, trace — з першого ретраю, а сам артефакт живе тиждень. Колись ми писали відео для всього підряд і за кілька днів уперлися в квоту. І ще одна дрібниця, яку я завжди перевіряю в пайплайні: крок завантаження повинен мати if: always(), інакше звіт зникає саме в той день, коли все впало.
🗣 How to say it out loud My rule is simple: every red failure in CI should arrive with evidence, and a green one should cost nothing. So I keep screenshots and videos only for failed tests, the trace from the first retry, and the artefact lives for a week. We once recorded video for everything and hit the quota within days. One more small thing I always check in a pipeline: the upload step needs if: always(), or the report disappears on precisely the day everything broke.
13 Що дає ReportPortal порівняно зі статичним HTML-звітом? What does ReportPortal add compared with a static HTML report?
Інструменти й артефакти звітностіReporting tools and artefacts Middle ▶

Що це: ReportPortal — сервер зі своєю базою, куди агенти раннерів у реальному часі надсилають запуски (launches), тести, логи і вкладення. Статичний звіт на кшталт Allure чи Playwright HTML — це знімок одного прогону; ReportPortal зберігає всі прогони разом і дає їх порівнювати.

Навіщо і коли: Головна фіча — класифікація падінь за типами дефектів: To Investigate, Product Bug, Automation Bug, System Issue, No Defect. Людина один раз розмічає падіння, а Auto-Analysis за схожістю логів пропонує той самий тип для нових падінь. Плюс історія тесту через десятки запусків, фільтри за атрибутами і віджети для дашбордів. Він окупається, коли прогонів багато, команд кілька і на розбір падінь щодня йде година-дві.

Приклад з проєкту: Команда з 4 000 API- і UI-тестів щодня мала 60–80 падінь. Через місяць розмітки Auto-Analysis сам класифікував близько двох третин, і в щотижневому звіті ми показували не «pass rate 94%», а «12 продуктових дефектів, 30 проблем автоматизації, 18 збоїв оточення».

Підводні камені: Це ще один сервіс з базою, оновленнями і бекапами, і комусь його треба підтримувати. Auto-Analysis вчиться лише на ручній розмітці: якщо все лишається в To Investigate, магії не буде. API-ключ не можна комітити в конфіг.

Уточнююче питання: «Коли ReportPortal — надлишок?» — для однієї команди з кількома сотнями тестів і щоденним прогоном зазвичай вистачає Allure з history.

What it is: ReportPortal is a server with its own database to which runner agents stream launches, tests, logs and attachments in real time. A static report such as Allure or the Playwright HTML report is a snapshot of one run; ReportPortal keeps every run together and lets you compare them.

Why and when: The headline feature is classifying failures by defect type: To Investigate, Product Bug, Automation Bug, System Issue, No Defect. A person labels a failure once, and Auto-Analysis suggests the same type for new failures with similar logs. On top of that you get test history across dozens of launches, filters by attributes and widgets for dashboards. It pays off when there are many runs, several teams and an hour or two of daily failure triage.

From a project: A team with 4,000 API and UI tests saw 60 to 80 failures a day. After a month of labelling, Auto-Analysis classified about two thirds on its own, and the weekly report stopped saying "pass rate 94%" and started saying "12 product bugs, 30 automation issues, 18 environment failures".

Pitfalls: It is one more service with a database, upgrades and backups, and someone has to own it. Auto-Analysis only learns from manual labelling: if everything stays in To Investigate, there is no magic. The API key must never be committed into the config.

Follow-up question: "When is ReportPortal overkill?" For a single team with a few hundred tests and one daily run, Allure with history is usually enough.

Агент ReportPortal для Playwright
The ReportPortal agent for Playwright
// playwright.config.ts
const rpConfig = {
  apiKey: process.env.RP_API_KEY,          // ключ лише зі змінної оточення
  endpoint: 'https://rp.example.com/api/v1',
  project: 'shop_e2e',
  launch: 'nightly-regression',
  attributes: [
    { key: 'stand', value: 'staging-2' },
    { key: 'build', value: process.env.BUILD_ID },
  ],
  description: 'Нічна регресія checkout і кабінету',
};

export default defineConfig({
  reporter: [['line'], ['@reportportal/agent-js-playwright', rpConfig]],
});
// playwright.config.ts
const rpConfig = {
  apiKey: process.env.RP_API_KEY,          // key only from an environment variable
  endpoint: 'https://rp.example.com/api/v1',
  project: 'shop_e2e',
  launch: 'nightly-regression',
  attributes: [
    { key: 'stand', value: 'staging-2' },
    { key: 'build', value: process.env.BUILD_ID },
  ],
  description: 'Nightly regression for checkout and the account area',
};

export default defineConfig({
  reporter: [['line'], ['@reportportal/agent-js-playwright', rpConfig]],
});
🔧 Що в артефакті: launch — назва запуску, за якою ReportPortal будує історію; attributes — пари ключ-значення для фільтрів і віджетів, наприклад стенд і білд. project має вже існувати на сервері.
🔧 What the artefact shows: launch is the launch name ReportPortal builds history on; attributes are key-value pairs for filters and widgets, such as stand and build. The project must already exist on the server.
🗣 Як читати вголос Я пояснюю різницю так: Allure показує мені один прогін, а ReportPortal — життя тесту за місяць. Найцінніше для мене — типи дефектів: коли ми почали розмічати падіння як продуктові, автоматизаційні або проблеми оточення, щотижневий звіт став відповідати на питання «що саме зламано». Але я не рекомендую його кожній команді: це окремий сервер, який хтось має підтримувати, і без дисципліни розмітки він перетворюється на дорогий Allure.
🗣 How to say it out loud I explain the difference like this: Allure shows me one run, ReportPortal shows me the life of a test over a month. What I value most is defect types: once we started labelling failures as product, automation or environment problems, the weekly report began answering "what exactly is broken". But I would not push it on every team: it is a separate server someone has to run, and without labelling discipline it turns into an expensive Allure.
14 Як зібрати QA-дашборд у Jira з JQL-фільтрів? How do you build a Jira QA dashboard from JQL filters?
Інструменти й артефакти звітностіReporting tools and artefacts Middle ▶

Що це: Дашборд у Jira — сторінка з гаджетами, кожен з яких працює поверх збереженого фільтра. Тож спершу пишуться і зберігаються JQL-запити, а вже потім на них накладаються гаджети: Filter Results (список), Two Dimensional Filter Statistics (таблиця, наприклад компонент × severity), Created vs. Resolved Chart (приплив і закриття в часі), Pie Chart (розподіл).

Навіщо і коли: Дашборд прибирає ручні зведення в Excel перед кожним стендапом і демо. Він потрібен, коли стейкхолдери щотижня питають «скільки відкритих критичних» і «чи встигаємо закривати», і відповідь має бути однакова для всіх.

Приклад з проєкту: Для команди з 9 розробників я зібрав дашборд із п'яти гаджетів. Created vs. Resolved за 30 днів показав, що з середини спринту створювали по 14 багів на тиждень, а закривали 6. Це стало аргументом на плануванні взяти в наступний спринт 20% ємності на борг, і за два спринти криві зійшлися.

Підводні камені: Severity — зазвичай кастомне поле, і назви значень у різних проєктах різні, тож фільтри ламаються при копіюванні. Гаджети бачать лише ті задачі, до яких має доступ глядач, тому менеджер і QA можуть бачити різні цифри. Фільтр без statusCategory тягне закриті задачі. І дашборд без власника за пів року заростає мертвими гаджетами.

Уточнююче питання: «Як відрізнити справжній ріст дефектів від того, що ми просто більше тестуємо?» — дивитись на приплив разом з обсягом тестування і на частку знайдених у проді, а не лише на кількість створених.

What it is: A Jira dashboard is a page of gadgets, each running on top of a saved filter. So you first write and save JQL queries, and then put gadgets over them: Filter Results (a list), Two Dimensional Filter Statistics (a table such as component × severity), Created vs. Resolved Chart (inflow versus closure over time), Pie Chart (a breakdown).

Why and when: A dashboard removes the hand-made Excel summaries before every stand-up and demo. It is needed once stakeholders ask each week "how many criticals are open" and "are we keeping up", and everyone should get the same answer.

From a project: For a team of nine developers I built a five-gadget dashboard. The 30-day Created vs. Resolved chart showed that from mid-sprint 14 bugs a week were being created and 6 closed. That became the argument at planning to reserve 20% of the next sprint for the backlog, and within two sprints the curves met.

Pitfalls: Severity is usually a custom field with value names that differ between projects, so filters break when copied. Gadgets only show issues the viewer has permission to see, so a manager and a tester can see different numbers. A filter without statusCategory drags in closed issues. And a dashboard with no owner fills up with dead gadgets within six months.

Follow-up question: "How do you tell a real rise in defects from simply testing more?" Look at inflow together with testing volume and the share found in production, not just the count of created bugs.

Збережені фільтри під гаджети
Saved filters behind the gadgets
# Відкриті баги поточного спринту за severity -> Two Dimensional Filter Statistics
project = SHOP AND issuetype = Bug AND sprint in openSprints()
  AND statusCategory != Done ORDER BY priority DESC

# Приплив і закриття -> Created vs. Resolved Chart (30 днів)
project = SHOP AND issuetype = Bug

# Знайдені в проді за 30 днів -> Filter Results
project = SHOP AND issuetype = Bug AND labels = found-in-prod AND created >= -30d

# Перевідкриті за 2 тижні -> Pie Chart за Component
project = SHOP AND issuetype = Bug
  AND status CHANGED FROM Done TO Reopened AFTER -14d
# Open bugs in the current sprint by severity -> Two Dimensional Filter Statistics
project = SHOP AND issuetype = Bug AND sprint in openSprints()
  AND statusCategory != Done ORDER BY priority DESC

# Inflow and closure -> Created vs. Resolved Chart (30 days)
project = SHOP AND issuetype = Bug

# Found in production over 30 days -> Filter Results
project = SHOP AND issuetype = Bug AND labels = found-in-prod AND created >= -30d

# Reopened within two weeks -> Pie Chart by Component
project = SHOP AND issuetype = Bug
  AND status CHANGED FROM Done TO Reopened AFTER -14d
🔧 Що в артефакті: Кожен запит зберігається як окремий фільтр, а гаджет обирає його і поле для групування. statusCategory != Done не залежить від назв статусів у workflow, а CHANGED FROM … TO … AFTER читає історію переходів, а не поточний статус.
🔧 What the artefact shows: Each query is saved as its own filter, and the gadget picks it plus a field to group by. statusCategory != Done does not depend on workflow status names, and CHANGED FROM … TO … AFTER reads the transition history rather than the current status.
🗣 Як читати вголос Я починаю дашборд не з гаджетів, а з питань, які мені щотижня ставлять: скільки відкритих критичних, чи встигаємо закривати, що прилетіло з проду. Під кожне питання — один збережений фільтр, під фільтр — один гаджет. Найкорисніший для мене — Created vs. Resolved: одного разу він наочно показав, що ми створюємо вдвічі більше багів, ніж закриваємо, і це переконало команду взяти час на борг краще за будь-які слова.
🗣 How to say it out loud I do not start a dashboard with gadgets; I start with the questions I get every week: how many criticals are open, are we keeping up, what came in from production. One saved filter per question, one gadget per filter. My favourite is Created vs. Resolved: once it showed plainly that we were creating twice as many bugs as we closed, and that convinced the team to set time aside for the backlog better than any words could.
15 Як побудувати дашборд якості в Grafana з даних CI і чого на ньому не має бути? How do you build a Grafana quality dashboard from CI data, and what should it leave out?
Інструменти й артефакти звітностіReporting tools and artefacts Senior ▶

Що це: Grafana сама нічого не зберігає: вона малює запити до джерела даних. Тому спершу потрібен конвеєр: після кожного прогону CI парсить JUnit XML або JSON-результати і пише рядки в таблицю PostgreSQL (прогін, тест, статус, тривалість, ретраї, гілка, коміт). Далі панелі — це SQL з макросами Grafana, наприклад $__timeFilter для обраного періоду.

Навіщо і коли: Jira бачить дефекти, CI бачить окремі прогони, а тренди за місяці — pass rate main-гілки, кількість нестабільних тестів, p95 тривалості набору — не видно ніде. Grafana потрібна, коли рішення про інвестиції в автоматизацію або інфраструктуру треба ухвалювати на даних, а не відчуттях.

Приклад з проєкту: Панель p95 тривалості показала, що регресія повільно зросла з 18 до 41 хвилини за квартал, а панель ретраїв — що половина росту дали 30 тестів із повторними спробами. Після їх виправлення прогін повернувся до 22 хвилин.

Підводні камені і чого не показувати: Панелі «тести на людину» чи «баги на розробника» швидко стають інструментом тиску і метрики починають підганяти. Одне велике число pass rate без знаменника — vanity panel. Не варто змішувати гілки фіч і main: червоні фіча-гілки — нормальна частина роботи. Кожна панель повинна відповідати на питання, яке хтось реально ставить, інакше її прибирають.

Уточнююче питання: «Як рахувати flaky на цих даних?» — тест, що в одному прогоні спершу впав, а на ретраї пройшов, або дав різні результати на тому самому коміті.

What it is: Grafana stores nothing itself; it draws queries against a data source. So you need a pipeline first: after each run CI parses the JUnit XML or JSON results and writes rows into a PostgreSQL table (run, test, status, duration, retries, branch, commit). The panels are then SQL with Grafana macros, for example $__timeFilter for the selected range.

Why and when: Jira sees defects and CI sees individual runs, but trends over months, such as main-branch pass rate, the number of unstable tests or the p95 suite duration, are visible nowhere. Grafana earns its place when investment decisions about automation or infrastructure have to be made on data rather than gut feeling.

From a project: A p95 duration panel showed that the regression had crept from 18 to 41 minutes over a quarter, and a retries panel showed that half of the growth came from 30 tests that kept retrying. Fixing those brought the run back to 22 minutes.

Pitfalls and what to leave out: Panels like "tests per person" or "bugs per developer" quickly turn into pressure tools, and people start gaming them. A single big pass-rate number with no denominator is a vanity panel. Do not mix feature branches with main: red feature branches are a normal part of the work. Every panel has to answer a question someone actually asks, or it goes.

Follow-up question: "How would you count flaky tests from this data?" A test that failed and then passed on retry within one run, or gave different outcomes on the same commit.

Таблиця прогонів і запит панелі в Grafana (PostgreSQL)
A test-run table and a Grafana panel query (PostgreSQL)
-- одна строка = один тест в одному прогоні
CREATE TABLE test_result (
  run_id      text,
  test_id     text,
  status      text,        -- passed / failed / skipped
  retries     int,
  duration_ms int,
  branch      text,
  started_at  timestamptz
);

-- панель: щоденний pass rate, нестабільні тести і p95 тривалості на main
SELECT
  $__timeGroupAlias(started_at, '1d'),
  round(100.0 * count(*) FILTER (WHERE status = 'passed')
        / nullif(count(*) FILTER (WHERE status <> 'skipped'), 0), 1) AS pass_rate,
  count(DISTINCT test_id) FILTER (WHERE status = 'passed' AND retries > 0) AS flaky_tests,
  percentile_cont(0.95) WITHIN GROUP (ORDER BY duration_ms) / 1000 AS p95_sec
FROM test_result
WHERE $__timeFilter(started_at) AND branch = 'main'
GROUP BY 1
ORDER BY 1;
-- one row = one test in one run
CREATE TABLE test_result (
  run_id      text,
  test_id     text,
  status      text,        -- passed / failed / skipped
  retries     int,
  duration_ms int,
  branch      text,
  started_at  timestamptz
);

-- panel: daily pass rate, unstable tests and p95 duration on main
SELECT
  $__timeGroupAlias(started_at, '1d'),
  round(100.0 * count(*) FILTER (WHERE status = 'passed')
        / nullif(count(*) FILTER (WHERE status <> 'skipped'), 0), 1) AS pass_rate,
  count(DISTINCT test_id) FILTER (WHERE status = 'passed' AND retries > 0) AS flaky_tests,
  percentile_cont(0.95) WITHIN GROUP (ORDER BY duration_ms) / 1000 AS p95_sec
FROM test_result
WHERE $__timeFilter(started_at) AND branch = 'main'
GROUP BY 1
ORDER BY 1;
🔧 Що в артефакті: $__timeGroupAlias(started_at, '1d') групує рядки по днях і називає колонку time, як чекає Grafana; $__timeFilter підставляє період з дашборду. Skipped не входить у знаменник pass rate, а «пройшов після ретраю» рахується як нестабільний.
🔧 What the artefact shows: $__timeGroupAlias(started_at, '1d') buckets rows by day and names the column time, as Grafana expects; $__timeFilter injects the dashboard's range. Skipped tests stay out of the pass-rate denominator, and "passed after a retry" counts as unstable.
🗣 Як читати вголос Для мене Grafana — це про тренди, яких не видно в окремому прогоні. Я кладу результати кожного прогону в Postgres і будую кілька панелей: pass rate на main, кількість тестів, що проходять лише з ретраю, і p95 тривалості. Одного разу саме тренд тривалості показав, що регресія за квартал виросла більше ніж удвічі. І я свідомо не роблю панелей «на людину» — щойно метрика стає оцінкою конкретного інженера, вона перестає говорити правду.
🗣 How to say it out loud For me Grafana is about trends you cannot see in a single run. I push every run's results into Postgres and build a few panels: pass rate on main, the number of tests that only pass on retry, and p95 duration. Once, the duration trend was what showed our regression had more than doubled in a quarter. And I deliberately avoid per-person panels: the moment a metric becomes a grade for a specific engineer, it stops telling the truth.
16 Як мітки, компоненти і зв'язки між задачами роблять звіти про тести і дефекти зручними для зрізів? How do labels, components and issue links make test and defect reporting sliceable?
Інструменти й артефакти звітностіReporting tools and artefacts Junior ▶

Що це: Це метадані задачі в трекері. Component — частина продукту (Checkout, Search, Auth), зазвичай фіксований список з власником. Labels — вільні теги для наскрізних ознак: regression, found-in-prod, automation-gap. Issue links з'єднують баг з історією чи вимогою, яку він порушує, і з тест-кейсом, що його ловить.

Навіщо і коли: Будь-яка метрика у звіті — це зріз: «скільки дефектів у Checkout», «скільки втекло в прод», «які історії спринту ще мають відкриті баги». Без послідовно заповнених полів такий зріз неможливий або його роблять вручну. Заповнювати їх треба в момент створення бага, а не перед звітом.

Приклад з проєкту: Команда домовилась про 6 обов'язкових міток і компонент на кожному баг-репорті. Через квартал один запит показав, що 11 із 17 дефектів з проду припали на компонент Payments і мали мітку automation-gap. Це прямо визначило, куди додати тести.

Підводні камені: Мітки чутливі до написання: regression, Regression і regresion — три різні значення, тож потрібен узгоджений список. Функція issueFunction — з плагіна ScriptRunner, а не стандартна JQL; на іншому інстансі запит просто не спрацює. Стандартна linkedIssues() є, але повертає зв'язки лише однієї задачі.

Уточнююче питання: «Чим компонент кращий за мітку?» — компонент має обмежений список значень і власника, тому для зрізу за частиною продукту він надійніший.

What it is: This is issue metadata in the tracker. A component is a part of the product (Checkout, Search, Auth), usually a fixed list with an owner. Labels are free tags for cross-cutting traits: regression, found-in-prod, automation-gap. Issue links connect a bug to the story or requirement it breaks and to the test case that catches it.

Why and when: Every metric in a report is a slice: "how many defects in Checkout", "how many escaped to production", "which sprint stories still have open bugs". Without consistently filled fields that slice is impossible or done by hand. The fields have to be filled when the bug is created, not right before the report.

From a project: The team agreed on six mandatory labels and a component on every bug. A quarter later, one query showed that 11 of 17 production defects were in the Payments component and carried the automation-gap label. That told us directly where to add tests.

Pitfalls: Labels are spelling-sensitive: regression, Regression and regresion are three separate values, so you need an agreed list. issueFunction comes from the ScriptRunner plugin, not standard JQL, so the query simply fails on another instance. The standard linkedIssues() does exist, but it returns the links of just one issue.

Follow-up question: "Why prefer a component over a label?" A component has a closed list of values and an owner, so it is the more reliable way to slice by product area.

JQL-зрізи за компонентом, міткою і зв'язком
JQL slices by component, label and link
# Дефекти з проду в Payments за квартал
project = SHOP AND issuetype = Bug AND component = Payments
  AND labels = found-in-prod AND created >= startOfMonth(-2)

# Ескейпи, для яких бракувало автотесту
project = SHOP AND issuetype = Bug AND labels in (found-in-prod)
  AND labels = automation-gap

# Усі баги, пов'язані з історією SHOP-120 (стандартна JQL-функція)
issuetype = Bug AND issue in linkedIssues(SHOP-120)
# Production defects in Payments over the quarter
project = SHOP AND issuetype = Bug AND component = Payments
  AND labels = found-in-prod AND created >= startOfMonth(-2)

# Escapes that lacked an automated test
project = SHOP AND issuetype = Bug AND labels in (found-in-prod)
  AND labels = automation-gap

# All bugs linked to story SHOP-120 (a standard JQL function)
issuetype = Bug AND issue in linkedIssues(SHOP-120)
🔧 Що в артефакті: startOfMonth(-2) дає початок місяця два місяці тому, тобто приблизно квартал; дві умови по labels вимагають обидві мітки одночасно. linkedIssues(SHOP-120) працює без плагінів і повертає задачі, пов'язані з цією історією.
🔧 What the artefact shows: startOfMonth(-2) is the start of the month two months back, roughly a quarter; two separate labels conditions require both labels at once. linkedIssues(SHOP-120) needs no plugin and returns the issues linked to that story.
🗣 Як читати вголос Я кажу, що хороший звіт починається не в день звіту, а в момент створення бага. Якщо компонент і мітки заповнені однаково, я одним запитом відповідаю, де ми найбільше пропускаємо в прод і чому. Колись саме так ми побачили, що більшість ескейпів — це платежі, де бракувало автотестів. А ще я завжди перевіряю, щоб запити були на стандартній JQL: запит зі ScriptRunner на іншому Jira просто не запрацює.
🗣 How to say it out loud I say a good report does not start on report day; it starts when the bug is created. If components and labels are filled consistently, one query tells me where we leak most into production and why. That is exactly how we once saw that most escapes were in payments, where automated tests were missing. I also make sure queries use standard JQL: a ScriptRunner query simply will not run on a different Jira.
▶ 3 10
17 Що таке тестові метрики і як їх групують? What are test metrics, and how are they grouped?
Метрики виконання, покриття і стабільностіExecution, coverage and stability metrics Junior ▶

Що це: Тестова метрика — це число, яке відповідає на конкретне питання про тестування чи продукт: скільки перевірено, скільки впало, де з'являються дефекти, як швидко їх виправляють. Без питання метрика перетворюється на цифру заради цифри.

Як групують: Найчастіше ділять на три групи. Процесні описують, як працює тестування: швидкість виконання, частка автоматизації, час на регрес. Продуктові описують сам продукт: кількість і щільність дефектів, leakage у прод, кількість інцидентів. Проєктні описують план: прогрес виконання проти графіка, витрачені години, залишок ризиків. Друга вісь — базові (прямо пораховані: 480 тестів виконано, 12 багів знайдено) і похідні (обчислені з базових: pass rate, defect density, DRE).

Приклад з проєкту: На мобільному банкінгу ми показували менеджменту три похідні метрики — прогрес регресу, відкриті critical і leakage за місяць, — а базові числа лишали в додатку до звіту для тих, хто хоче перевірити розрахунок.

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

Уточнююче питання: «Яку одну метрику ви б залишили, якби можна було лише одну?» Гарна відповідь — ту, що найближча до користувача, наприклад leakage, з поясненням чому.

What it is: A test metric is a number that answers a specific question about testing or the product: how much was checked, how much failed, where defects appear and how fast they get fixed. Without a question behind it, a metric is just a figure on a slide.

How they are grouped: The usual split has three groups. Process metrics describe how testing works: execution speed, automation share, time spent on regression. Product metrics describe the product itself: defect count and density, leakage to production, incidents. Project metrics describe the plan: execution progress against schedule, hours spent, risks still open. A second axis is base metrics, counted directly (480 tests executed, 12 bugs found), versus derived ones computed from them (pass rate, defect density, DRE).

From a project: On a mobile banking app we showed management three derived numbers — regression progress, open criticals and monthly leakage — and kept the base counts in an appendix for anyone who wanted to check the maths.

Pitfalls: Collecting everything. Nobody reads twenty charts, and every metric depends on people filling Jira or the TMS in a consistent way.

Follow-up question: "If you could keep only one metric, which would it be?" A strong answer picks the one closest to users, such as leakage, and explains why.

Метрики за групами
Metrics by group
Група       | Метрика              | Тип      | Формула / джерело
------------+----------------------+----------+-----------------------------------------
Процесні    | Час регресу          | базова   | тривалість пайплайна, хв
Процесні    | Частка автоматизації | похідна  | автотести / усі кейси регресу * 100
Продуктові  | Defect density       | похідна  | дефекти / KLOC або / story points
Продуктові  | Leakage              | похідна  | баги з проду / усі баги релізу * 100
Проєктні    | Прогрес виконання    | похідна  | виконано / заплановано * 100
Проєктні    | Відкриті critical    | базова   | JQL-фільтр, штук

# Приклад: 312 із 400 запланованих кейсів виконано -> 312 / 400 = 78%
Group    | Metric             | Kind     | Formula / source
---------+--------------------+----------+-----------------------------------------
Process  | Regression time    | base     | pipeline duration, min
Process  | Automation share   | derived  | automated / all regression cases * 100
Product  | Defect density     | derived  | defects / KLOC or / story points
Product  | Leakage            | derived  | prod bugs / all bugs of release * 100
Project  | Execution progress | derived  | executed / planned * 100
Project  | Open criticals     | base     | JQL filter, count

# Example: 312 of 400 planned cases executed -> 312 / 400 = 78%
🔧 Що в артефакті: Таблиця поєднує дві осі: групу (процес, продукт, проєкт) і тип (базова чи похідна). Колонка з формулою одразу показує, з яких сирих даних рахується кожне число.
🔧 What the artefact shows: The table combines both axes: the group (process, product, project) and the kind (base or derived). The formula column shows which raw data each number is built from.
💡 Простими словами: Базова метрика — те, що порахував руками чи фільтром; похідна — дріб із двох базових.
💡 In plain words: A base metric is what you count directly; a derived metric is a fraction built from two base ones.
🗣 Як читати вголос Я розкладаю метрики на три кошики: як ми тестуємо, що з продуктом і чи встигаємо за планом. Плюс розрізняю сирі числа і ті, що з них вираховуються, бо відсоток без знаменника нічого не каже. На практиці я показую керівництву максимум три-чотири похідні цифри, а сирі дані тримаю поруч, щоб будь-хто міг перерахувати. І перед тим, як додати нову метрику, питаю себе, яке рішення вона допоможе прийняти.
🗣 How to say it out loud I sort metrics into three buckets: how we test, what state the product is in, and whether we are on track with the plan. I also separate raw counts from the ratios built on them, because a percentage without its denominator tells you nothing. In practice I show leadership three or four derived numbers at most and keep the raw data next to them so anyone can recompute. Before adding a new metric I ask what decision it will help someone make.
18 Що показує pass rate і чому сам по собі він вводить в оману? What does pass rate show, and why does it mislead on its own?
Метрики виконання, покриття і стабільностіExecution, coverage and stability metrics Junior ▶

Що це: Pass rate — частка тестів, що пройшли, від певної бази. Питання завжди в базі: від виконаних чи від запланованих. Від цього залежить, чи буде в звіті 95,9% чи 94%.

Навіщо і коли: Метрика зручна для щоденного статусу і трендів між білдами: якщо вчора було 97%, а сьогодні 88%, щось зламалося. Але вона нічого не каже про те, що саме впало. Один провалений тест оплати важить більше за двадцять тестів на тултіпи.

Приклад з проєкту: У релізі було 500 запланованих кейсів: 470 пройшли, 20 впали, 10 заблоковані через недоступний платіжний сендбокс. Від виконаних (490) виходить 95,9%, від запланованих — 94%. Ми показували обидва числа і окремо рядок «заблоковано: 10, причина — сендбокс».

Підводні камені: Skipped і blocked тести тихо випадають зі знаменника. Якщо 60 тестів позначити як skip, то 420 пройдених із 440 виконаних дають 95,5%, хоча від повного набору це лише 84%. Ще одна пастка — однакова вага для всіх тестів і повторні запуски, які перетворюють червоний тест на зелений.

Уточнююче питання: «98% пройшло, можна релізити?» Відповідь — залежить від того, що в тих 2%: подивитися на пріоритет упалих тестів і відкриті критичні дефекти, а не на відсоток.

What it is: Pass rate is the share of tests that passed against some base. The whole question is which base: executed tests or planned ones. That choice decides whether the report says 95.9% or 94%.

Why and when: It works well for a daily status and for trends between builds: 97% yesterday and 88% today means something broke. It says nothing about what failed, though. One failed payment test outweighs twenty failed tooltip checks.

From a project: A release had 500 planned cases: 470 passed, 20 failed and 10 were blocked because the payment sandbox was down. Against executed tests (490) that is 95.9%, against planned ones it is 94%. We reported both numbers plus a separate line: "blocked: 10, cause: sandbox".

Pitfalls: Skipped and blocked tests silently drop out of the denominator. Mark 60 tests as skipped and 420 passed out of 440 executed gives 95.5%, while against the full suite it is only 84%. Equal weight for every test and retries that turn a red test green are two more traps.

Follow-up question: "98% passed, can we release?" The answer is that it depends on the 2%: look at the priority of the failed tests and the open critical defects, not at the percentage.

Розрахунок pass rate з двома знаменниками
Pass rate with two denominators
Заплановано 500 | Пройшло 470 | Впало 20 | Заблоковано 10

Pass rate від виконаних   = 470 / (470 + 20)  = 470 / 490 = 95,9%
Pass rate від запланованих = 470 / 500              = 94,0%
Прогрес виконання          = 490 / 500              = 98,0%

# Як skip роздуває цифру
Набір 500, skip 60, пройшло 420, впало 20
Від виконаних:   420 / 440 = 95,5%
Від повного набору: 420 / 500 = 84,0%
Planned 500 | Passed 470 | Failed 20 | Blocked 10

Pass rate of executed = 470 / (470 + 20) = 470 / 490 = 95.9%
Pass rate of planned  = 470 / 500               = 94.0%
Execution progress    = 490 / 500               = 98.0%

# How skips inflate the number
Suite 500, skipped 60, passed 420, failed 20
Of executed:   420 / 440 = 95.5%
Of full suite: 420 / 500 = 84.0%
🔧 Що в артефакті: Той самий прогін дає різні відсотки залежно від знаменника. Друга частина показує, як масовий skip піднімає pass rate на 11,5 пункту без жодного виправлення.
🔧 What the artefact shows: The same run gives different percentages depending on the denominator. The second part shows how a mass skip lifts pass rate by 11.5 points without a single fix.
🗣 Як читати вголос Pass rate — перша цифра, яку всі дивляться, і найлегша для самообману. Я завжди уточнюю, від чого рахую: від виконаних чи від запланованих, і окремо пишу, скільки заблоковано і скільки пропущено. Бо якщо непомітно вимкнути шістдесят тестів, відсоток красиво росте. І ще я ніколи не даю цьому числу вирішувати реліз — дивлюся, які саме тести червоні і що вони перевіряють.
🗣 How to say it out loud Pass rate is the first number everyone looks at and the easiest one to fool yourself with. I always say what I divide by — executed or planned — and I list blocked and skipped tests separately. If you quietly switch off sixty tests the percentage goes up nicely. I also never let this number decide a release on its own; I look at which tests are red and what they actually cover.
19 Що таке тестове покриття у звітності і які його види бувають? What is test coverage in the context of reporting, and which kinds are there?
Метрики виконання, покриття і стабільностіExecution, coverage and stability metrics Middle ▶

Що це: Покриття — це відповідь на питання «яку частину того, що мало бути перевірено, ми перевірили». Залежно від того, що вважати «цілим», покриття буває різним, і в звіті треба прямо писати, про яке йдеться.

Види: Покриття вимог — частка user stories чи вимог, до яких прив'язаний хоча б один тест (а краще — виконаний). Покриття ризиків — частка ризиків із ризик-реєстру, для яких є перевірка. Покриття коду — частка рядків чи гілок, виконаних під час тестів. Покриття конфігурацій — частка комбінацій браузерів, ОС чи пристроїв, на яких реально прогнали набір.

Приклад з проєкту: У релізі маркетплейсу було 120 stories, до 102 прив'язані тести — 85%. Із 18 ризиків високого рівня перевірки мали 15 — 83,3%. Матриця 4 браузери × 3 ОС дає 12 комбінацій, прогнали 7 — 58,3%. Саме остання цифра пояснила, чому баг у Safari на macOS прослизнув у прод.

Підводні камені: Змішувати види покриття в одне число. Писати «покриття 85%» без уточнення. Рахувати тест «прив'язаним», навіть якщо він не запускався в цьому релізі.

Уточнююче питання: «Яке покриття важливіше показати бізнесу?» Здебільшого покриття ризиків, бо воно говорить мовою наслідків, а не рядків коду.

What it is: Coverage answers "what share of what should have been checked did we actually check". Depending on what counts as the whole, coverage means different things, and a report has to say explicitly which kind it shows.

Kinds: Requirements coverage is the share of user stories or requirements with at least one linked test (ideally an executed one). Risk coverage is the share of items in the risk register that have a check. Code coverage is the share of lines or branches executed by tests. Configuration coverage is the share of browser, OS or device combinations the suite really ran on.

From a project: A marketplace release had 120 stories and 102 of them had linked tests: 85%. Of 18 high-level risks, 15 had checks: 83.3%. A matrix of 4 browsers × 3 operating systems gives 12 combinations and we ran 7: 58.3%. That last number explained why a Safari-on-macOS bug slipped into production.

Pitfalls: Blending the kinds into one number. Writing "coverage 85%" with no qualifier. Counting a test as linked even though it never ran in this release.

Follow-up question: "Which coverage matters most to the business?" Usually risk coverage, because it talks in consequences rather than lines of code.

Блок покриття в тест-звіті
Coverage block in a test report
Покриття — реліз 3.8.0
-----------------------------------------------------------
Вимоги       102 / 120 stories з тестами        = 85,0%
             96 / 120 з виконаними тестами      = 80,0%
Ризики       15 / 18 високих ризиків            = 83,3%
Конфігурації 7 / 12 (4 браузери x 3 ОС)         = 58,3%
Код (unit)   7 800 / 10 400 рядків               = 75,0%
-----------------------------------------------------------
Не покрито: Safari/macOS, Firefox/Windows, Edge/Linux ...
Coverage — release 3.8.0
-----------------------------------------------------------
Requirements  102 / 120 stories with tests       = 85.0%
              96 / 120 with executed tests       = 80.0%
Risks         15 / 18 high risks                 = 83.3%
Configs       7 / 12 (4 browsers x 3 OS)         = 58.3%
Code (unit)   7,800 / 10,400 lines                = 75.0%
-----------------------------------------------------------
Not covered: Safari/macOS, Firefox/Windows, Edge/Linux ...
🔧 Що в артефакті: Кожен вид покриття має власний знаменник і рядок у звіті. Для вимог показано два числа: «є тест» і «тест виконано», бо між ними часто різниця в кілька відсотків.
🔧 What the artefact shows: Each kind of coverage has its own denominator and its own line. Requirements get two numbers, "has a test" and "test executed", because the gap between them is often a few percent.
🗣 Як читати вголос Коли мене питають про покриття, я спершу перепитую: покриття чого? Вимог, ризиків, коду чи конфігурацій — це чотири різні відповіді. У звітах я розписую їх окремими рядками з чесними знаменниками. Мені запам'ятався реліз, де все виглядало добре, але з дванадцяти комбінацій браузерів ми прогнали сім, і саме в непрогнаній вилізла проблема. Відтоді непокриті конфігурації я перелічую явно.
🗣 How to say it out loud When someone asks me about coverage, my first question back is: coverage of what? Requirements, risks, code and configurations are four different answers. In reports I give each its own line with an honest denominator. I remember a release where everything looked fine, but we had run seven of twelve browser combinations and the problem showed up in one we skipped. Since then I list uncovered configurations explicitly.
20 Як звітувати про покриття вимог на основі матриці трасування? How do you report requirements coverage from a traceability matrix?
Метрики виконання, покриття і стабільностіExecution, coverage and stability metrics Middle ▶

Що це: Матриця трасування (RTM) зв'язує кожну вимогу чи story з тест-кейсами, результатом їх останнього запуску і відкритими дефектами. Для звіту з неї беруть не саму таблицю, а висновки: скільки вимог покрито, скільки покрито і пройшло, де покрито, але падає.

Навіщо і коли: Перед go/no-go і в кінці спринту. Менеджера цікавить не «скільки тестів зелених», а «які функції готові». RTM відповідає саме на це: PAY-104 має три тести, один червоний і відкритий critical, отже функція не готова.

Приклад з проєкту: У Jira з Xray кожна story мала статус покриття: OK, NOK, NOTRUN або UNCOVERED. Ми вивантажили це по fixVersion і отримали шість stories: три OK, дві NOK, одна UNCOVERED. У TestRail те саме дає поле References у кейсах і звіт покриття за посиланнями. У звіт пішли рядки NOK з номерами багів і рядок UNCOVERED з поясненням, чому тестів немає.

Підводні камені: Вважати вимогу покритою, бо до неї прив'язано кейс, який ніхто не запускав. Не оновлювати зв'язки, коли story розбивають на дві. Пхати в звіт усю матрицю на сотню рядків.

Уточнююче питання: «Що робити, якщо в кінці спринту знайшлася story без тестів?» Не малювати покриття — записати її як відомий ризик і сказати, хто і коли її перевірить.

What it is: A requirements traceability matrix (RTM) links each requirement or story to its test cases, their latest result and open defects. What goes into a report is not the table itself but the conclusions: how many requirements are covered, how many are covered and passing, and where something is covered yet failing.

Why and when: Before a go/no-go and at the end of a sprint. A manager wants to know which features are ready, not how many tests are green. The RTM answers exactly that: PAY-104 has three tests, one red, plus an open critical, so the feature is not ready.

From a project: In Jira with Xray every story had a coverage status: OK, NOK, NOTRUN or UNCOVERED. We exported it by fixVersion and got six stories: three OK, two NOK, one UNCOVERED. In TestRail the References field on cases and the coverage-for-references report give the same picture. The report listed the NOK rows with bug keys and the UNCOVERED row with the reason it had no tests.

Pitfalls: Treating a requirement as covered because a case is linked that nobody ran. Not updating links when a story is split in two. Pasting a hundred-row matrix into the report.

Follow-up question: "What if a story turns up without tests at the end of the sprint?" Do not dress up the coverage — record it as a known risk and say who will check it and when.

Фрагмент RTM і підсумок для звіту
RTM excerpt and the report summary
Story    | Тест-кейси            | Останній прогін   | Відкриті баги   | Статус
---------+-----------------------+-------------------+-----------------+----------
PAY-101  | TC-11, TC-12          | 2 пройшли         | —               | OK
PAY-102  | TC-13                 | пройшов           | —               | OK
PAY-103  | TC-14, TC-15          | 2 пройшли         | PAY-331 (minor) | OK
PAY-104  | TC-16, TC-17, TC-18   | 2 пройшли, 1 впав | PAY-340 (crit)  | NOK
PAY-105  | TC-19                 | впав              | PAY-342 (major) | NOK
PAY-106  | —                     | —                 | —               | UNCOVERED

Покрито тестами:        5 / 6 = 83,3%
Покрито і пройшло:      3 / 6 = 50,0%
Покрито, але падає:     PAY-104, PAY-105
Без тестів:             PAY-106 (експорт CSV, тестує Олена до 14.05)
Story    | Test cases            | Latest run        | Open bugs       | Status
---------+-----------------------+-------------------+-----------------+----------
PAY-101  | TC-11, TC-12          | 2 passed          | —               | OK
PAY-102  | TC-13                 | passed            | —               | OK
PAY-103  | TC-14, TC-15          | 2 passed          | PAY-331 (minor) | OK
PAY-104  | TC-16, TC-17, TC-18   | 2 passed, 1 fail  | PAY-340 (crit)  | NOK
PAY-105  | TC-19                 | failed            | PAY-342 (major) | NOK
PAY-106  | —                     | —                 | —               | UNCOVERED

Covered by tests:       5 / 6 = 83.3%
Covered and passing:    3 / 6 = 50.0%
Covered but failing:    PAY-104, PAY-105
No tests:               PAY-106 (CSV export, Olena to test by 14 May)
🔧 Що в артефакті: Верхня частина — сама матриця зі статусами у стилі Xray. Нижня — чотири рядки, які реально йдуть у звіт: два відсотки, список «покрито, але падає» і непокрита story з власником.
🔧 What the artefact shows: The top part is the matrix with Xray-style statuses. The bottom part is the four lines that actually go into the report: two percentages, the covered-but-failing list and the uncovered story with an owner.
🗣 Як читати вголос Матрицю трасування я сприймаю як джерело, а не як слайд. Зі звіту менеджер має за хвилину зрозуміти, які фічі готові, а які ні. Тому я даю два відсотки — скільки вимог має тести і скільки має зелені тести — і окремо перелічую ті, де тест є, але падає. Якщо щось взагалі не покрито, я не ховаю це, а пишу, хто й коли це перевірить. Так звіт лишається чесним навіть тоді, коли новина неприємна.
🗣 How to say it out loud I treat the traceability matrix as a source, not as a slide. From the report a manager should see within a minute which features are ready and which are not. So I give two percentages — how many requirements have tests and how many have green tests — and separately list the ones where a test exists but fails. If something has no coverage at all, I do not hide it; I write who will check it and when.
21 Що показує покриття коду і які в нього межі? What does code coverage tell you, and what are its limits?
Метрики виконання, покриття і стабільностіExecution, coverage and stability metrics Middle ▶

Що це: Покриття коду — частка коду, яку виконали тести: рядки (line), інструкції (statement), гілки умов (branch), функції. Його рахують інструменти на кшталт JaCoCo для JVM чи Istanbul/nyc для JavaScript.

Що воно каже насправді: Низьке покриття — надійний сигнал: код, який жодного разу не виконувався, точно не перевірений. Високе покриття нічого не гарантує, бо інструмент бачить лише, що рядок виконався, а не те, що результат перевірили. Тест без жодного assert може дати 100% рядків.

Приклад з проєкту: Сервіс розрахунку знижок мав 92% рядків і лише 62,5% гілок. Різниця вказала на умови з кількома вітками, де тестували тільки щасливий шлях. Саме в непокритій гілці «знижка більша за суму кошика» сидів баг з від'ємною ціною. Після цього ми замінили глобальний поріг на перевірку покриття змінених рядків у PR: diff-cover з порогом 80% лише для нового коду.

Підводні камені: Робити загальний відсоток KPI — команда почне писати тести без перевірок. Дивитися тільки на line coverage. Рахувати покриття коду як покриття вимог.

Уточнююче питання: «Як перевірити, що тести справді щось перевіряють?» Мутаційне тестування (PIT, Stryker): якщо мутант вижив, асерту не вистачає.

What it is: Code coverage is the share of code executed by tests: lines, statements, condition branches, functions. Tools such as JaCoCo for the JVM or Istanbul/nyc for JavaScript measure it.

What it really says: Low coverage is a reliable signal: code that never ran is definitely untested. High coverage guarantees nothing, because the tool only sees that a line executed, not that anyone checked the result. A test with no assertion at all can give 100% line coverage.

From a project: A discount service had 92% line coverage but only 62.5% branch coverage. The gap pointed at multi-way conditions where only the happy path was exercised. The uncovered branch "discount larger than cart total" held a bug that produced a negative price. After that we replaced the global threshold with a check on changed lines in each PR: diff-cover with an 80% bar for new code only.

Pitfalls: Making the overall percentage a KPI, which leads to tests without checks. Looking at line coverage alone. Treating code coverage as requirements coverage.

Follow-up question: "How do you verify that tests actually check something?" Mutation testing (PIT, Stryker): a surviving mutant means a missing assertion.

Звіт nyc, тест без перевірок і гейт на змінені рядки
nyc summary, an assert-free test and a changed-lines gate
# npx nyc report --reporter=text-summary
Statements   : 91.2% ( 1824/2000 )
Branches     : 62.5% ( 250/400 )
Functions    : 88.0% ( 176/200 )
Lines        : 92.0% ( 1748/1900 )

// covers every line of applyDiscount, verifies nothing
test('applies discount', () => {
  applyDiscount({ total: 50 }, { percent: 120 });
});

# PR gate: only lines changed against main must be covered
diff-cover coverage.xml --compare-branch=origin/main --fail-under=80
🔧 Що в артефакті: Підсумок nyc показує розрив між рядками (92%) і гілками (62,5%). Тест посередині виконує код, але нічого не стверджує. Остання команда diff-cover читає Cobertura XML і падає, якщо змінені в PR рядки покриті менше ніж на 80%.
🔧 What the artefact shows: The nyc summary shows the gap between lines (92%) and branches (62.5%). The test in the middle runs the code but asserts nothing. The final diff-cover command reads Cobertura XML and fails when lines changed in the PR are less than 80% covered.
🗣 Як читати вголос Покриття коду я читаю в один бік: низьке — точно погано, високе — ще нічого не означає. Інструмент бачить, що рядок виконався, але не бачить, чи хтось перевірив результат. Тому я дивлюся на гілки, а не на рядки, і пропоную гейт на змінений код у пулреквесті замість магічних вісімдесяти відсотків на весь репозиторій. А якщо треба довести, що тести чогось варті, згадую мутаційне тестування.
🗣 How to say it out loud I read code coverage in one direction only: low is definitely bad, high does not mean much yet. The tool sees that a line ran, not whether anyone checked the result. So I look at branches rather than lines, and I suggest a gate on changed code in the pull request instead of a magic eighty percent for the whole repository. If I need to prove the tests are worth anything, I bring up mutation testing.
22 Що таке flaky-тест і як виміряти рівень нестабільності? What is a flaky test, and how do you measure the flakiness rate?
Метрики виконання, покриття і стабільностіExecution, coverage and stability metrics Middle ▶

Що це: Flaky-тест на тому самому коді (тому самому коміті) то проходить, то падає. Ключ — «той самий коміт»: якщо результат змінився після зміни коду, це може бути нормальна реакція тесту, а не нестабільність.

Як виміряти: Потрібна історія запусків: тест, коміт, статус, час. Далі для кожної пари «тест + коміт» дивимося, чи були там і passed, і failed. Рівень нестабільності тесту — частка комітів, на яких він «мигав». Рівень для набору — частка тестів, що мигнули хоча б раз за період. Ще одна корисна цифра — скільки червоних пайплайнів були червоними тільки через flaky.

Приклад з проєкту: Ми писали результати з JUnit XML у Postgres. За 14 днів 38 із 1 250 тестів мали хоча б один коміт з обома результатами — 3,04% набору. Із 180 запусків main 27 були червоними тільки через такі тести — 15% пайплайнів, тобто кожен сьомий «червоний» був хибною тривогою. Найгірший тест мигав на 9 із 20 комітів.

Підводні камені: Рахувати flaky на різних комітах. Вважати все, що пройшло з ретраю, нормою. Не відрізняти нестабільний тест від справжнього періодичного бага в продукті.

Уточнююче питання: «Що ви робите з тестом, у якого 45% нестабільності?» Виводжу в карантин з власником і терміном, а не просто додаю ретраї.

What it is: A flaky test passes and fails on the same code, meaning the same commit. The same commit is the key part: if the result changed after a code change, the test may simply be doing its job.

How to measure it: You need run history: test, commit, status, timestamp. For every test-and-commit pair, check whether both passed and failed occurred. A test's flakiness rate is the share of commits where it flipped. The suite rate is the share of tests that flipped at least once in the period. Another useful figure is how many red pipelines were red only because of flaky tests.

From a project: We loaded JUnit XML results into Postgres. Over 14 days 38 of 1,250 tests had at least one commit with both outcomes, which is 3.04% of the suite. Of 180 runs on main, 27 were red solely because of those tests: 15% of pipelines, so one in seven red builds was a false alarm. The worst test flipped on 9 of 20 commits.

Pitfalls: Counting flips across different commits. Treating anything that passed on retry as fine. Not telling a flaky test apart from a genuinely intermittent product bug.

Follow-up question: "What do you do with a test that is 45% flaky?" Quarantine it with an owner and a deadline instead of adding more retries.

SQL: тести, що мигали на тому самому коміті (PostgreSQL)
SQL: tests that flipped on the same commit (PostgreSQL)
-- test_runs(test_id, commit_sha, status, started_at)
WITH per_commit AS (
  SELECT test_id, commit_sha,
         COUNT(DISTINCT status) AS outcomes
  FROM test_runs
  WHERE started_at >= NOW() - INTERVAL '14 days'
    AND status IN ('passed', 'failed')
  GROUP BY test_id, commit_sha
)
SELECT test_id,
       COUNT(*) FILTER (WHERE outcomes > 1) AS flaky_commits,
       COUNT(*)                              AS commits,
       ROUND(100.0 * COUNT(*) FILTER (WHERE outcomes > 1) / COUNT(*), 1) AS flaky_pct
FROM per_commit
GROUP BY test_id
HAVING COUNT(*) FILTER (WHERE outcomes > 1) > 0
ORDER BY flaky_pct DESC;

-- suite rate: 38 flaky tests / 1250 tests = 3.04%
-- pipeline noise: 27 flaky-only red runs / 180 runs = 15%
🔧 Що в артефакті: CTE групує запуски за парою тест + коміт і рахує різні статуси; більше одного статусу означає мигання. Зовнішній запит дає відсоток нестабільних комітів для кожного тесту, а коментарі внизу — два зведені показники.
🔧 What the artefact shows: The CTE groups runs by test and commit and counts distinct statuses; more than one status means a flip. The outer query gives each test's share of flaky commits, and the comments at the end hold the two summary figures.
🗣 Як читати вголос Для мене flaky — це тест, який на тому самому коміті дає різний результат, і саме так я його й рахую. Складаю історію запусків у базу, групую за тестом і комітом і дивлюся, де траплялися і зелений, і червоний статус. Звідси два числа: скільки тестів нестабільні і скільки червоних збірок були фальшивими. Друге зазвичай переконує команду краще, бо всі пам'ятають, скільки часу втратили на перезапуски.
🗣 How to say it out loud For me a flaky test is one that gives different results on the same commit, and that is exactly how I count it. I store run history in a database, group by test and commit and look for places with both a green and a red status. That gives two numbers: how many tests are unstable and how many red builds were fake. The second one usually convinces the team faster, because everyone remembers the time lost on reruns.
23 Як не дати flaky-тестам отруїти тестові звіти? How do you keep flaky tests from poisoning test reports?
Метрики виконання, покриття і стабільностіExecution, coverage and stability metrics Senior ▶

Проблема: Коли кожна третя збірка червона без причини, люди перестають читати звіти і тиснуть «перезапустити». Тоді справжнє падіння теж ігнорують. Нестабільність шкодить не тільки часу, а й довірі до всього сигналу.

Що роблю: По-перше, окремий статус. Playwright із ретраями позначає тест, що впав і потім пройшов, як flaky, і в JSON-звіті є окремий лічильник stats.flaky. Allure теж показує позначку flaky з історії. Ці числа йдуть у звіт окремим рядком, а не ховаються серед зелених. По-друге, карантин: тест з тегом @quarantine виконується в окремому проєкті, який не блокує мердж, але має власника і термін — наприклад, 10 робочих днів на виправлення або видалення. По-третє, ліміт: якщо в карантині понад 2% набору, нові тести туди не беруть, доки не розберуть старі.

Приклад з проєкту: Після запуску карантину частка хибних червоних збірок на main упала з 15% до 3% за місяць, а 11 з 38 тестів ми видалили як дублікати.

Компроміс: Ретраї рятують конвеєр, але приховують сигнал; без ретраїв сигнал чесний, але конвеєр постійно стоїть. Тому ретраї лише в CI, максимум два, а кожен flaky-результат рахується.

Уточнююче питання: «А якщо тест flaky, бо продукт сам нестабільний?» Тоді це баг продукту, його заводять і тест у карантин не відправляють.

The problem: When every third build is red for no reason, people stop reading reports and just hit rerun. Then a real failure gets ignored too. Flakiness costs time, but it also costs trust in the whole signal.

What I do: First, a separate status. With retries enabled, Playwright marks a test that failed and then passed as flaky, and its JSON report has a dedicated stats.flaky counter. Allure also shows a flaky marker from history. These numbers go into the report on their own line instead of hiding among the greens. Second, quarantine: a test tagged @quarantine runs in a separate project that does not block merges, but it has an owner and a deadline, say ten working days to fix or delete. Third, a cap: if quarantine holds more than 2% of the suite, nothing new goes in until old entries are cleared.

From a project: After quarantine went live, false red builds on main dropped from 15% to 3% in a month, and we deleted 11 of 38 tests as duplicates.

Trade-off: Retries keep the pipeline moving but hide the signal; no retries keeps the signal honest but the pipeline keeps stalling. So retries run only in CI, two at most, and every flaky result is counted.

Follow-up question: "What if the test is flaky because the product itself is unstable?" Then it is a product bug: file it, and do not quarantine the test.

Конфіг Playwright з карантином і правила
Playwright config with quarantine and the rules
// playwright.config.ts
import { defineConfig } from '@playwright/test';

export default defineConfig({
  retries: process.env.CI ? 2 : 0,   // пройшов після ретраю -> статус flaky
  reporter: [['html'], ['json', { outputFile: 'results.json' }]],
  projects: [
    { name: 'gate',       grepInvert: /@quarantine/ },  // блокує мердж
    { name: 'quarantine', grep: /@quarantine/ },        // лише звітує
  ],
});

// Правила карантину
// 1. Кожен тест у карантині має власника і тікет.
// 2. 10 робочих днів: виправити або видалити.
// 3. Карантин > 2% набору -> нові тести не додаємо.
// 4. У звіті окремий рядок: flaky = stats.flaky з results.json.
// playwright.config.ts
import { defineConfig } from '@playwright/test';

export default defineConfig({
  retries: process.env.CI ? 2 : 0,   // passed after a retry -> status flaky
  reporter: [['html'], ['json', { outputFile: 'results.json' }]],
  projects: [
    { name: 'gate',       grepInvert: /@quarantine/ },  // blocks merge
    { name: 'quarantine', grep: /@quarantine/ },        // reports only
  ],
});

// Quarantine rules
// 1. Every quarantined test has an owner and a ticket.
// 2. 10 working days: fix it or delete it.
// 3. Quarantine > 2% of the suite -> no new entries.
// 4. The report has its own line: flaky = stats.flaky from results.json.
🔧 Що в артефакті: Два проєкти ділять набір за тегом: gate блокує мердж, quarantine лише звітує. Ретраї ввімкнені тільки в CI, тож тест, що пройшов із другої спроби, потрапляє в лічильник flaky, а не губиться серед зелених.
🔧 What the artefact shows: Two projects split the suite by tag: gate blocks merges, quarantine only reports. Retries are on in CI only, so a test that passes on the second attempt lands in the flaky counter instead of disappearing among the greens.
🗣 Як читати вголос Найбільша шкода від flaky не в перезапусках, а в тому, що люди перестають вірити червоному. Тому я роблю нестабільність видимою: окремий статус у звіті, окремий рядок у підсумку. Нестабільні тести йдуть у карантин, але з власником, тікетом і дедлайном, інакше карантин стає кладовищем. Ретраї я дозволяю лише в CI і не більше двох, і кожен випадок рахую. Якщо ж мигає сам продукт — це баг, а не проблема тесту.
🗣 How to say it out loud The worst damage from flaky tests is not the reruns, it is that people stop believing red. So I make flakiness visible: its own status in the report, its own line in the summary. Unstable tests go to quarantine, but with an owner, a ticket and a deadline, otherwise quarantine turns into a graveyard. I allow retries only in CI, two at most, and I count every case. If the product itself is what flickers, that is a bug, not a test problem.
24 Які метрики автоматизації варто відстежувати: час виконання, стабільність, вартість підтримки? Which automation metrics are worth tracking: execution time, stability, maintenance cost?
Метрики виконання, покриття і стабільностіExecution, coverage and stability metrics Middle ▶

Що це: Метрики автоматизації показують, чи набір автотестів ще допомагає, чи вже заважає. Кількість тестів тут майже нічого не каже; важливіше, як швидко вони дають відповідь, наскільки їй можна вірити і скільки годин ідуть на їхнє утримання.

Що відстежувати: Час виконання: медіана і p95 тривалості набору, бо середнє ховає повільні прогони. Стабільність: частка flaky і частка хибних червоних збірок. Вартість підтримки: години на лагодження тестів відносно всіх годин на автоматизацію. Динаміка набору: скільки тестів додали і скільки прибрали за тиждень. І корисність: скільки справжніх дефектів зловили автотести за період.

Приклад з проєкту: За шість тижнів p95 регресу виріс з 26 до 41 хвилини, а частка підтримки дійшла до 30%. Розбір показав, що новий логін через SSO додавав по 20 секунд на тест. Ми перейшли на збережений стан авторизації, і p95 повернувся до 24 хвилин.

Підводні камені: Рахувати «кількість автотестів» як успіх. Не вести облік годин на підтримку — тоді автоматизація здається безкоштовною. Дивитися на один тиждень, а не на тренд.

Уточнююче питання: «Яка частка підтримки нормальна?» Чіткої норми немає; тривожний сигнал — стабільне зростання тиждень до тижня.

What it is: Automation metrics tell you whether the test suite still helps or has started getting in the way. The number of tests says almost nothing here; what matters is how fast the suite answers, how far you can trust the answer and how many hours go into keeping it alive.

What to track: Execution time: median and p95 suite duration, because an average hides the slow runs. Stability: flaky share and the share of false red builds. Maintenance cost: hours spent fixing tests relative to all automation hours. Suite churn: tests added and removed per week. And usefulness: how many real defects the automated tests caught in the period.

From a project: Over six weeks regression p95 grew from 26 to 41 minutes and the maintenance share reached 30%. The analysis showed that a new SSO login added about 20 seconds to every test. We switched to a saved authentication state and p95 went back to 24 minutes.

Pitfalls: Treating the number of automated tests as success. Not logging maintenance hours, so automation looks free. Looking at a single week instead of the trend.

Follow-up question: "What maintenance share is normal?" There is no fixed norm; the warning sign is steady week-on-week growth.

Тижневий тренд метрик автоматизації
Weekly automation metrics trend
Тиждень | Тести | p50   | p95   | Flaky | Лагодження, год | Додано/прибрано | Спіймано багів
--------+-------+-------+-------+-------+-----------------+-----------------+---------------
W14     | 1180  | 18 хв | 26 хв | 2,1%  | 6               | +22 / -4        | 5
W16     | 1215  | 21 хв | 31 хв | 2,8%  | 11              | +19 / -2        | 3
W18     | 1248  | 27 хв | 38 хв | 3,5%  | 19              | +17 / -1        | 4
W19     | 1250  | 29 хв | 41 хв | 3,6%  | 24              | +3 / -1         | 2

# Частка підтримки (W19) = години на лагодження / усі години автоматизації
#                        = 24 / 80 (2 інженери x 40 год) = 30%
Week | Tests | p50    | p95    | Flaky | Fix hours | Added/removed | Bugs caught
-----+-------+--------+--------+-------+-----------+---------------+------------
W14  | 1180  | 18 min | 26 min | 2.1%  | 6         | +22 / -4      | 5
W16  | 1215  | 21 min | 31 min | 2.8%  | 11        | +19 / -2      | 3
W18  | 1248  | 27 min | 38 min | 3.5%  | 19        | +17 / -1      | 4
W19  | 1250  | 29 min | 41 min | 3.6%  | 24        | +3 / -1       | 2

# Maintenance share (W19) = fix hours / all automation hours
#                         = 24 / 80 (2 engineers x 40 h) = 30%
🔧 Що в артефакті: Таблиця показує тренд, а не знімок: тривалість і години на лагодження ростуть, а кількість нових тестів падає. Формула внизу переводить години в частку, яку легко порівнювати між тижнями.
🔧 What the artefact shows: The table shows a trend rather than a snapshot: duration and fix hours climb while new tests dry up. The formula at the bottom turns hours into a share that is easy to compare week to week.
🗣 Як читати вголос Я не хвалюся кількістю автотестів, бо тисяча тестів, які йдуть годину і постійно мигають, — це тягар. Я дивлюся на чотири речі: скільки чекаємо на результат, наскільки йому віримо, скільки годин іде на ремонт і чи ловлять тести щось реальне. Найкраще працює тижневий тренд: коли видно, що p95 повзе вгору, а ремонт з'їдає третину часу, розмова про рефакторинг стає дуже предметною.
🗣 How to say it out loud I do not brag about the number of automated tests, because a thousand tests that take an hour and keep flickering are a burden. I watch four things: how long we wait for a result, how much we trust it, how many hours go into repairs and whether the tests catch anything real. A weekly trend works best: once people see p95 creeping up and repairs eating a third of the time, the refactoring conversation becomes very concrete.
25 Як порахувати ROI автоматизації тестування і коли це число нечесне? How do you calculate test automation ROI, and when is that number dishonest?
Метрики виконання, покриття і стабільностіExecution, coverage and stability metrics Senior ▶

Формула: ROI = (заощаджене − вкладене) / вкладене. Заощаджене — це ручна робота, яку автотести замінили, мінус те, що вони самі коштують у підтримці. Вкладене — години на побудову фреймворку й тестів.

Приклад з проєкту: Ручний регрес займав 3 дні у двох тестувальників: 48 годин на реліз, 24 релізи на рік — 1 152 години. Після автоматизації лишився день ручного дослідницького тестування одним тестувальником (8 годин × 24 = 192) і близько 6 годин підтримки на реліз (144 на рік). Економія — 1 152 − 192 − 144 = 816 годин на рік. Побудова коштувала 600 годин. ROI першого року: (816 − 600) / 600 = 36%. Окупність: 816 / 24 = 34 години економії на реліз, 600 / 34 ≈ 17,6, тобто на 18-му релізі, приблизно через 9 місяців.

Коли число нечесне: Коли рахують «години ручного виконання» для тестів, які вручну ніхто б і не запускав. Коли забувають підтримку, інфраструктуру CI і час на розбір flaky-падінь. Коли в набір входять тести, що за рік не впали жодного разу через реальний баг. І навпаки — формула не бачить головного виграшу: відповідь за 20 хвилин замість трьох днів, що дає змогу релізити частіше.

Уточнююче питання: «ROI вийшов від'ємним — автоматизацію закривати?» Не обов'язково: спершу подивитися, що саме дорого (нестабільні UI-тести?), і перенести перевірки нижче по піраміді.

The formula: ROI = (savings − investment) / investment. Savings are the manual work the automated tests replaced, minus what the tests themselves cost to maintain. Investment is the hours spent building the framework and the tests.

From a project: Manual regression took two testers three days: 48 hours per release, 24 releases a year, 1,152 hours. After automation, one tester still spent a day on exploratory testing (8 hours × 24 = 192) and maintenance took about 6 hours per release (144 a year). Savings: 1,152 − 192 − 144 = 816 hours a year. Building it cost 600 hours. First-year ROI: (816 − 600) / 600 = 36%. Break-even: 816 / 24 = 34 hours saved per release, 600 / 34 ≈ 17.6, so on the 18th release, about nine months in.

When the number is dishonest: When it counts "manual execution hours" for tests nobody would ever have run by hand. When it forgets maintenance, CI infrastructure and time spent triaging flaky failures. When the suite includes tests that did not fail once in a year because of a real bug. And the other way round: the formula misses the biggest gain, an answer in 20 minutes instead of three days, which is what lets a team release more often.

Follow-up question: "ROI came out negative — should we drop automation?" Not necessarily: first find what is expensive (flaky UI tests?) and move those checks lower in the pyramid.

Розрахунок ROI і точки окупності
ROI and break-even calculation
Вхідні дані (на рік, 24 релізи)
  Ручний регрес:       2 тестувальники x 3 дні x 8 год = 48 год/реліз -> 1152 год
  Лишилось вручну:     1 x 1 день x 8 год = 8 год/реліз              ->  192 год
  Підтримка автотестів: 6 год/реліз                                   ->  144 год
  Побудова фреймворку: 600 год (одноразово)

Економія за рік = 1152 - 192 - 144 = 816 год
ROI рік 1      = (816 - 600) / 600 = 0,36 -> 36%
ROI рік 2      = (816 * 2 - 600) / 600 = 1,72 -> 172%
Окупність      = 600 / (816 / 24) = 600 / 34 = 17,6 -> 18-й реліз (~9 міс.)

# Не входить у формулу: швидкість зворотного зв'язку (20 хв проти 3 днів),
# час на розбір flaky, тести, що за рік не зловили жодного бага
Inputs (per year, 24 releases)
  Manual regression:   2 testers x 3 days x 8 h = 48 h/release -> 1152 h
  Still manual:        1 x 1 day x 8 h = 8 h/release            ->  192 h
  Test maintenance:    6 h/release                              ->  144 h
  Framework build:     600 h (one-off)

Savings per year = 1152 - 192 - 144 = 816 h
ROI year 1       = (816 - 600) / 600 = 0.36 -> 36%
ROI year 2       = (816 * 2 - 600) / 600 = 1.72 -> 172%
Break-even       = 600 / (816 / 24) = 600 / 34 = 17.6 -> 18th release (~9 months)

# Not in the formula: feedback speed (20 min vs 3 days),
# time spent triaging flaky tests, tests that caught no bug all year
🔧 Що в артефакті: Розрахунок іде від годин, а не від грошей, тож його легко перевірити. ROI другого року рахується накопичено: дві річні економії проти одноразових 600 годин. Коментар унизу перелічує те, що формула не бачить.
🔧 What the artefact shows: The calculation works in hours rather than money, so anyone can check it. Year-two ROI is cumulative: two years of savings against the one-off 600 hours. The comment at the bottom lists what the formula cannot see.
🗣 Як читати вголос ROI я рахую в годинах, бо їх легко перевірити: скільки йшло на ручний регрес, скільки лишилось, скільки з'їдає підтримка і скільки коштувала побудова. У моєму прикладі окупність настає десь на вісімнадцятому релізі. Але я завжди кажу, що ця цифра неповна. Вона не бачить, що відповідь приходить за двадцять хвилин, і водночас легко роздувається, якщо рахувати тести, які вручну ніхто б не проганяв.
🗣 How to say it out loud I calculate ROI in hours because anyone can check them: how much manual regression cost, how much is still manual, how much maintenance eats and what the build cost. In my example it pays back around the eighteenth release. But I always add that the number is incomplete. It cannot see that the answer now arrives in twenty minutes, and it is easy to inflate by counting tests nobody would ever have run by hand.
26 Що означає регресія в тестовому звіті і як її показати? What does regression mean in a test report, and how do you show it?
Метрики виконання, покриття і стабільностіExecution, coverage and stability metrics Junior ▶

Що це: У звітності регресія — це те, що працювало в попередньому білді й перестало працювати в поточному. Для звіту важливо не просто кількість упалих тестів, а саме нові падіння: тести, які на білді N−1 були зелені, а на білді N стали червоні.

Навіщо і коли: Регресії найнебезпечніші для релізу: користувачі вже звикли, що функція працює, і зламана стара функція болить сильніше, ніж недороблена нова. Тому в звіті по регресійному прогону роблять окремий розділ «нові падіння порівняно з попереднім білдом», із номером білду, де тест востаннє пройшов.

Приклад з проєкту: Білд 2.14.0 проти 2.13.2: із 640 тестів регресу 9 впали, з них 4 — нові. Два нові падіння в пошуку, одне в експорті, одне в налаштуваннях сповіщень. Решта 5 падали й раніше через відомі баги. У звіті ми виділили саме чотири нові й указали, в якому діапазоні комітів шукати причину. Allure показує це з коробки, якщо зберігати історію між запусками.

Підводні камені: Змішувати нові й давні падіння в одне число. Порівнювати з білдом, де частину тестів не запускали. Не показувати тренд: кількість нових регресій за кілька релізів поспіль.

Уточнююче питання: «Звідки брати попередній результат?» З історії Allure, збережених JUnit XML або з бази результатів.

What it is: In reporting, a regression is something that worked in the previous build and stopped working in the current one. The report should not just count failed tests; it should call out new failures: tests that were green on build N−1 and turned red on build N.

Why and when: Regressions are the riskiest thing for a release: users already rely on the feature, and a broken old feature hurts more than an unfinished new one. So a regression run report gets its own section, "new failures compared to the previous build", with the build where each test last passed.

From a project: Build 2.14.0 against 2.13.2: 9 of 640 regression tests failed, and 4 of those were new. Two new failures were in search, one in export and one in notification settings. The other 5 had been failing before because of known bugs. The report singled out the four new ones and named the commit range to look at. Allure shows this out of the box when history is kept between runs.

Pitfalls: Merging new and old failures into one number. Comparing with a build where part of the suite did not run. Not showing the trend: new regressions across several releases in a row.

Follow-up question: "Where does the previous result come from?" From Allure history, stored JUnit XML files or a results database.

Розділ регресії в тест-звіті
Regression section of a test report
Регресія: білд 2.14.0 проти 2.13.2 (640 тестів)
Впало всього: 9 | Нові падіння: 4 | Відомі (з відкритими багами): 5

Нові падіння
Тест                               | Область       | Востаннє пройшов | Баг
-----------------------------------+---------------+------------------+---------
search_filters_by_price_range      | Пошук         | 2.13.2           | SRCH-512
search_sorting_keeps_page          | Пошук         | 2.13.2           | SRCH-513
export_orders_csv_utf8             | Експорт       | 2.13.2           | EXP-88
notifications_email_toggle_saved   | Налаштування  | 2.13.1           | NTF-47

Діапазон комітів для пошуку причини: a41f9c2..e07d3b1
Тренд нових регресій: 2.12 -> 1, 2.13 -> 2, 2.14 -> 4
Regression: build 2.14.0 vs 2.13.2 (640 tests)
Failed total: 9 | New failures: 4 | Known (open bugs): 5

New failures
Test                               | Area          | Last passed | Bug
-----------------------------------+---------------+-------------+---------
search_filters_by_price_range      | Search        | 2.13.2      | SRCH-512
search_sorting_keeps_page          | Search        | 2.13.2      | SRCH-513
export_orders_csv_utf8             | Export        | 2.13.2      | EXP-88
notifications_email_toggle_saved   | Settings      | 2.13.1      | NTF-47

Commit range to investigate: a41f9c2..e07d3b1
New regressions trend: 2.12 -> 1, 2.13 -> 2, 2.14 -> 4
🔧 Що в артефакті: Шапка розділяє нові й відомі падіння. Таблиця дає для кожного нового падіння область, останній зелений білд і завведений баг, а два рядки внизу — діапазон комітів і тренд за три релізи.
🔧 What the artefact shows: The header separates new failures from known ones. The table gives each new failure its area, last green build and filed bug, and the two lines at the bottom add the commit range and a three-release trend.
🗣 Як читати вголос Коли я пишу звіт по регресу, мене найбільше цікавить не скільки впало, а що впало вперше. Старі падіння з відкритими багами всі вже знають, а нові — це те, що зламали саме в цьому білді. Тому я роблю окремий блок: тест, область, де востаннє був зелений і номер бага. І додаю тренд за кілька релізів, бо якщо нових регресій щоразу більше, це вже розмова про процес, а не про один баг.
🗣 How to say it out loud When I write a regression report, what interests me most is not how much failed but what failed for the first time. Everyone already knows the old failures with open bugs; the new ones are what this build broke. So I add a separate block: the test, the area, the last green build and the bug key. I also add a trend across a few releases, because if new regressions keep growing, that is a process conversation rather than a single bug.
▶ 4 11
27 Які поля баг-репорту треба заповнювати однаково, щоб метрики дефектів були достовірними? Which bug fields must be filled consistently for defect metrics to be trustworthy?
Метрики дефектівDefect metrics Junior ▶

Що це: Кожна метрика дефектів — це просто запит до трекера, згрупований за якимось полем. Leakage рахується за полем «де знайдено», density — за компонентом, розподіл за важливістю — за severity. Якщо поле порожнє або кожен заповнює його по-своєму, графік показує не якість продукту, а дисципліну заповнення. Як написати сам репорт (заголовок, кроки, expected/actual, вкладення), детально розібрано в гайді «Bug Reporting»; тут мова про поля, з яких потім будуються цифри.

Навіщо і коли: Мінімальний набір для звітності: severity і priority за письмовою шкалою команди, environment, affected version (build, де знайдено) і fix version, component, фаза виявлення (QA, UAT, production), root cause після виправлення, labels за домовленим словником і лінк на user story або вимогу. Лінк дає трасованість: видно, які історії найбільш «дірявлять».

Приклад з проєкту: У звіті за квартал leakage раптом упав до 1%. Виявилося, що в 34 з 41 продакшн-бага поле Found in лишилося порожнім, бо баги заводила підтримка через іншу форму. Після того як поле стало обов'язковим на екрані створення, реальний показник виявився 7%.

Підводні камені: Вільний текст замість списку значень (Prod, prod, production) розбиває одну категорію на три. Root cause, який заповнює QA замість розробника, часто вгадується.

Уточнююче питання: «Як змусити команду заповнювати поля?» — зробити їх обов'язковими у workflow на конкретному переході, наприклад root cause при переході в Resolved, і показувати на дашборді кількість багів із порожніми полями.

What it is: Every defect metric is just a tracker query grouped by some field. Leakage is counted by the "found in" field, density by component, the severity split by severity. If a field is empty or everyone fills it differently, the chart shows data-entry discipline rather than product quality. How to write the report itself (title, steps, expected/actual, attachments) is covered in detail in the Bug Reporting guide; this card is about the fields the numbers are built from.

Why and when: The minimum for reporting: severity and priority on the team's written scale, environment, affected version (the build where it was found) and fix version, component, detection phase (QA, UAT, production), root cause once fixed, labels from an agreed vocabulary, and a link to the user story or requirement. The link gives traceability: you can see which stories leak the most.

From a project: In a quarterly report leakage suddenly dropped to 1%. It turned out that 34 of the 41 production bugs had an empty Found in field because support filed them through a different form. Once the field became mandatory on the create screen, the real figure turned out to be 7%.

Pitfalls: Free text instead of a value list (Prod, prod, production) splits one category into three. A root cause filled in by QA instead of the developer is often a guess.

Follow-up question: "How do you make the team fill the fields?" Make them required in the workflow on a specific transition, for example root cause on the move to Resolved, and show the count of bugs with empty fields on the dashboard.

Чек-лист полів для звітності
Reporting field checklist
Поле              Тип          Обов'язкове коли       Метрика, що від нього залежить
Severity          список       створення              розподіл за важливістю, exit criteria
Priority          список       triage                 черга виправлень
Environment       список       створення              дефекти за середовищем
Affected version  версія       створення              density на реліз, регресії
Fix version       версія       Resolved               що увійде в реліз
Component         список       створення              density за модулем
Found in          QA/UAT/Prod  створення              leakage, DRE
Root cause        список       Resolved               Pareto-аналіз причин
Labels            словник      за потреби             зрізи (regression, a11y, perf)
Link to story     issue link   створення              трасованість, покриття вимог

# Контроль якості даних (JQL):
project = SHOP AND issuetype = Bug AND "Found in[Dropdown]" is EMPTY
Field             Type         Required when          Metric that depends on it
Severity          list         create                 severity split, exit criteria
Priority          list         triage                 fix queue
Environment       list         create                 defects per environment
Affected version  version      create                 density per release, regressions
Fix version       version      Resolved               what ships in the release
Component         list         create                 density per module
Found in          QA/UAT/Prod  create                 leakage, DRE
Root cause        list         Resolved               root cause Pareto
Labels            vocabulary   as needed              slices (regression, a11y, perf)
Link to story     issue link   create                 traceability, requirement coverage

# Data quality check (JQL):
project = SHOP AND issuetype = Bug AND "Found in[Dropdown]" is EMPTY
🔧 Що в артефакті: Таблиця зв'язує кожне поле з метрикою, яка без нього зламається, і з моментом, коли його треба вимагати. Останній рядок — JQL, що знаходить баги з порожнім кастомним полем; назва поля в лапках залежить від вашого Jira.
🔧 What the artefact shows: The table ties each field to the metric that breaks without it and to the moment it should be required. The last line is JQL that finds bugs with an empty custom field; the quoted field name depends on your Jira instance.
💡 Простими словами: Метрика не краща за поле, з якого її рахують: порожнє поле — це дірка в графіку.
💡 In plain words: A metric is only as good as the field it is counted from: an empty field is a hole in the chart.
🗣 Як читати вголос Я завжди дивлюсь на баг-трекер як на базу даних для звітів. Якщо поле «де знайдено» порожнє, то leakage я порахувати чесно не можу, хоч би який гарний графік був. Тому з командою домовляюся про фіксований список значень, робимо ключові поля обов'язковими на потрібному переході у workflow, а на дашборді тримаю окремий лічильник багів з незаповненими полями. Коли він нульовий, цифрам уже можна вірити.
🗣 How to say it out loud I treat the bug tracker as the database behind every report. If the "found in" field is empty, I simply cannot count leakage honestly, however nice the chart looks. So I agree a fixed list of values with the team, we make the key fields required on the right workflow transition, and I keep a separate counter on the dashboard for bugs with missing fields. Once that counter sits at zero, the numbers become something I can actually trust.
28 Що таке defect density і як її правильно рахувати? What is defect density, and how do you calculate it correctly?
Метрики дефектівDefect metrics Middle ▶

Що це: Defect density — кількість підтверджених дефектів, поділена на розмір того, що тестували. Класична одиниця — дефекти на 1000 рядків коду (KLOC); в Agile-командах частіше рахують на story point, на фічу або на модуль. Сама по собі цифра мало що каже; цінність у порівнянні модулів між собою або одного модуля між релізами.

Навіщо і коли: Density показує, куди спрямувати регресію, code review і рефакторинг. Модуль із щільністю втричі вищою за середню — кандидат на додаткові тести і на розмову з архітектором. Метрика корисна на ретро релізу і при плануванні тестового покриття.

Приклад з проєкту: За реліз у Checkout знайшли 30 дефектів на 12 KLOC — 2,5 на KLOC. У Catalog 36 дефектів, більше в абсолютних цифрах, але на 40 KLOC — лише 0,9. Без нормалізації менеджмент хотів «посилити» Catalog; після розрахунку додаткові тести і парний review пішли в Checkout, і наступний реліз там дав 1,1 на KLOC.

Підводні камені: Дублікати і «Not a Bug» у чисельнику завищують показник. Рядки коду — погана міра розміру: згенерований код і конфіги роздувають знаменник. Порівнювати density між командами з різною культурою заведення багів некоректно: команда, яка чесно фіксує все, виглядатиме гірше. І найгірше — ставити density ціллю для розробників: баги почнуть закривати в чаті без тікетів.

Уточнююче питання: «Яку одиницю розміру обрати?» — ту, що стабільна у вашій команді: KLOC для зрілого моноліту, story points або кількість змінених файлів для команди, що часто рефакторить.

What it is: Defect density is the number of confirmed defects divided by the size of what was tested. The classic unit is defects per 1000 lines of code (KLOC); Agile teams more often count per story point, per feature or per module. The figure alone says little; the value is in comparing modules with each other or one module across releases.

Why and when: Density shows where to aim regression, code review and refactoring. A module with three times the average density is a candidate for extra tests and a talk with the architect. The metric is useful in a release retro and when planning test coverage.

From a project: In one release Checkout had 30 defects on 12 KLOC, which is 2.5 per KLOC. Catalog had 36 defects, more in absolute terms, but on 40 KLOC that is only 0.9. Without normalising, management wanted to "reinforce" Catalog; after the calculation the extra tests and pair review went to Checkout, and the next release there came in at 1.1 per KLOC.

Pitfalls: Duplicates and "Not a Bug" in the numerator inflate the figure. Lines of code are a poor size measure: generated code and configs bloat the denominator. Comparing density across teams with different bug-filing habits is unfair, because the team that honestly logs everything looks worse. Worst of all is turning density into a target for developers: bugs start getting fixed in chat with no ticket.

Follow-up question: "Which size unit should you choose?" Whichever is stable for your team: KLOC for a mature monolith, story points or changed files for a team that refactors a lot.

Формула і розрахунок за модулями
Formula and per-module calculation
Defect density = підтверджені дефекти / розмір

Модуль     Дефекти   KLOC   На KLOC
Checkout   30        12     30 / 12 = 2,50
Catalog    36        40     36 / 40 = 0,90
Profile     4         8      4 /  8 = 0,50
Разом      70        60     70 / 60 = 1,17

# Альтернатива для Agile: на story point
Спринт 42: 21 дефект / 42 SP = 0,5 дефекту на SP
# У чисельник НЕ йдуть: Duplicate, Not a Bug, Cannot Reproduce
Defect density = confirmed defects / size

Module     Defects   KLOC   Per KLOC
Checkout   30        12     30 / 12 = 2.50
Catalog    36        40     36 / 40 = 0.90
Profile     4         8      4 /  8 = 0.50
Total      70        60     70 / 60 = 1.17

# Agile alternative: per story point
Sprint 42: 21 defects / 42 SP = 0.5 defects per SP
# Excluded from the numerator: Duplicate, Not a Bug, Cannot Reproduce
🔧 Що в артефакті: Таблиця показує, чому абсолютна кількість багів вводить в оману: у Catalog їх більше, але щільність майже втричі нижча, ніж у Checkout. Рядок «Разом» дає середнє, з яким порівнюють кожен модуль.
🔧 What the artefact shows: The table shows why a raw bug count misleads: Catalog has more bugs, yet its density is almost three times lower than Checkout's. The total row gives the average each module is compared against.
🗣 Як читати вголос Density я використовую тільки для порівняння, ніколи як абсолютну оцінку. У мене був випадок, коли менеджер бачив 36 багів у каталозі і хотів туди кинути людей, а після ділення на розмір стало видно, що проблемний саме checkout, де коду втричі менше. Я завжди прибираю з чисельника дублікати й «не баг», і чесно кажу, що між різними командами цю цифру порівнювати не варто.
🗣 How to say it out loud I only use density to compare things, never as an absolute score. I once had a manager looking at 36 bugs in the catalog and wanting to move people there, but once we divided by size it was obvious the real problem was checkout, which has a third of the code. I always strip duplicates and not-a-bug tickets out of the numerator, and I say openly that this number shouldn't be compared across different teams.
29 Що таке defect leakage (escape rate) і як його рахують? What is defect leakage (escape rate), and how is it calculated?
Метрики дефектівDefect metrics Middle ▶

Що це: Defect leakage — частка дефектів, які пройшли повз тестування і були знайдені вже після релізу: користувачами, підтримкою чи моніторингом. Такий баг називають escape defect або escaped defect. Формула: дефекти, знайдені після релізу, поділені на всі дефекти цього релізу (до і після), помножено на 100.

Навіщо і коли: Це одна з небагатьох метрик, що вимірює результат роботи QA, а не активність. Рахують на реліз або за період, із фіксованим вікном спостереження, наприклад 30 днів після виходу, інакше старі релізи постійно «доростають».

Приклад з проєкту: Реліз 3.4: QA і UAT знайшли 76 дефектів, за 30 днів після виходу в продакшн — ще 4. Leakage = 4 / (76 + 4) × 100 = 5%. Два з чотирьох були в інтеграції з платіжним провайдером, якої не було на стейджингу — це і стало темою ретро.

Підводні камені: Перше — рахувати лише баги з тікетами: інциденти, які підтримка закрила сама, випадають. Друге — не розрізняти важливість: 5% тривіальних опечаток і 5% критичних збоїв оплати — різні історії, тому leakage варто показувати окремо для Critical і Major. Третє — зараховувати до escape дефекти, що існували в продакшні до релізу: у поля «affected version» має бути версія, де баг з'явився.

Уточнююче питання: «Чи може leakage бути 0%?» — може, але це привід перевірити дані: або вікно замале, або баги з продакшну не доходять до трекера.

What it is: Defect leakage is the share of defects that slipped past testing and were found after release: by users, support or monitoring. Such a bug is called an escape defect or escaped defect. The formula: defects found after release, divided by all defects of that release (before and after), times 100.

Why and when: It is one of the few metrics that measures the outcome of QA work rather than activity. You calculate it per release or per period, with a fixed observation window such as 30 days after launch, otherwise old releases keep "growing".

From a project: Release 3.4: QA and UAT found 76 defects, and within 30 days in production another 4 turned up. Leakage = 4 / (76 + 4) × 100 = 5%. Two of the four were in the payment provider integration, which did not exist on staging, and that became the retro topic.

Pitfalls: First, counting only bugs with tickets: incidents that support closed on their own drop out. Second, ignoring severity: 5% trivial typos and 5% critical payment failures are different stories, so show leakage separately for Critical and Major. Third, counting as escapes the defects that were already in production before the release: the affected version field must hold the version where the bug appeared.

Follow-up question: "Can leakage be 0%?" It can, but it is a reason to check the data: either the window is too short, or production bugs are not reaching the tracker.

Формула і JQL для релізу 3.4
Formula and JQL for release 3.4
Defect leakage % = escaped defects / (pre-release defects + escaped defects) x 100
Release 3.4:      4 / (76 + 4) x 100 = 5.0 %

-- escaped defects (numerator)
project = SHOP AND issuetype = Bug AND affectedVersion = "3.4"
  AND labels = found-in-prod AND created >= "2026-06-02" AND created <= "2026-07-02"

-- all defects of the release (denominator)
project = SHOP AND issuetype = Bug AND affectedVersion = "3.4"
  AND resolution not in (Duplicate, "Cannot Reproduce")
🔧 Що в артефакті: Перший запит рахує баги, знайдені в продакшні за 30-денне вікно після релізу, другий — усі підтверджені баги цієї версії. Ділення першого числа на друге дає leakage; дублікати виключені, щоб не роздувати знаменник.
🔧 What the artefact shows: The first query counts bugs found in production during the 30-day window after release, the second counts all confirmed bugs of that version. Dividing the first number by the second gives leakage; duplicates are excluded so they do not inflate the denominator.
🗣 Як читати вголос Leakage для мене — головний показник того, чи наше тестування ловить те, що важливо. Я рахую його на кожен реліз із фіксованим вікном у тридцять днів і завжди розбиваю за severity, бо одна втрачена оплата важить більше, ніж десять опечаток. І якщо раптом бачу нуль, то першим ділом іду перевіряти, чи баги з підтримки взагалі потрапляють у Jira.
🗣 How to say it out loud For me leakage is the main signal of whether our testing catches what matters. I calculate it per release with a fixed thirty-day window, and I always split it by severity, because one lost payment outweighs ten typos. And if I ever see a zero, the first thing I do is check whether bugs from support are even making it into Jira.
30 Як зменшити defect leakage — що саме змінювати в процесі? How do you reduce defect leakage — what exactly do you change?
Метрики дефектівDefect metrics Middle ▶

Що це: Зменшення leakage починається не з «тестувати більше», а з escape analysis: для кожного бага з продакшну відповісти, чому тестування його пропустило. Відповідей зазвичай чотири: тесту не було; тест був, але з невдалими даними; середовище відрізнялося від продакшну; вимога була неповна або змінилася.

Навіщо і коли: Кожна причина має свою дію, і вона не завжди в зоні QA. Відсутній тест — додати кейс і, якщо сценарій стабільний, автотест. Погані дані — розширити набір граничних значень. Розрив у середовищах — питання до DevOps (конфіги, фіче-флаги, інтеграції). Прогалина у вимогах — зміни в refinement і acceptance criteria. Аналіз роблять після кожного релізу для Critical і Major.

Приклад з проєкту: За квартал ми розібрали 9 escape-дефектів: 3 без тесту, 2 через дані (кирилиця в імені на квитанції), 3 через середовище (на стейджингу був вимкнений фіче-флаг, увімкнений у проді) і 1 через вимогу. Найдешевшою виявилась зміна для середовищ: синхронізація флагів перед регресією. Наступного кварталу escape-дефектів було 4.

Підводні камені: Шукати винного замість причини: люди почнуть ховати баги. Додавати тест на кожен escape без аналізу: регресія розростається, а клас проблем лишається. Не повертатися до дій: таблиця без виконаних пунктів нічого не змінює.

Уточнююче питання: «Що робити, якщо більшість escape — через вимоги?» — залучати QA до refinement, писати приклади (specification by example) і рев'ювити acceptance criteria до початку розробки.

What it is: Reducing leakage starts not with "test more" but with escape analysis: for every production bug, answer why testing missed it. There are usually four answers: there was no test; there was a test but with weak data; the environment differed from production; the requirement was incomplete or changed.

Why and when: Each cause has its own action, and it is not always within QA's reach. Missing test: add a case and, if the scenario is stable, an automated test. Weak data: widen the set of boundary values. Environment gap: a question for DevOps (configs, feature flags, integrations). Requirement gap: changes to refinement and acceptance criteria. The analysis is done after every release for Critical and Major bugs.

From a project: Over a quarter we went through 9 escaped defects: 3 had no test, 2 were data-related (Cyrillic in a name on a receipt), 3 were environment gaps (a feature flag switched off on staging but on in production) and 1 was a requirement gap. The cheapest fix turned out to be the environment one: syncing flags before the regression run. The next quarter had 4 escapes.

Pitfalls: Looking for someone to blame instead of a cause: people will start hiding bugs. Adding a test for every escape without analysis: the regression suite bloats while the class of problem remains. Never coming back to the actions: a table with no completed items changes nothing.

Follow-up question: "What if most escapes come from requirements?" Bring QA into refinement, write concrete examples (specification by example) and review acceptance criteria before development starts.

Таблиця escape analysis за квартал
Quarterly escape analysis table
Баг       Severity  Чому пропустили             Дія                                   Власник  Статус
SHOP-812  Critical  Флаг вимкнений на stage     Синхронізувати флаги перед регресією  DevOps   зроблено
SHOP-829  Major     Немає тесту на повернення   Кейс + API-автотест refund            QA       зроблено
SHOP-834  Major     Дані: кирилиця в імені      Додати юнікод-імена в тестові дані    QA       зроблено
SHOP-851  Major     Вимога: ліміт не описаний   AC з прикладами на refinement         PO + QA  в роботі

Підсумок: без тесту 3 | дані 2 | середовище 3 | вимоги 1  (разом 9)
Bug       Severity  Why it was missed           Action                                Owner    Status
SHOP-812  Critical  Flag off on staging         Sync flags before regression          DevOps   done
SHOP-829  Major     No test for refunds         Test case + refund API test           QA       done
SHOP-834  Major     Data: Cyrillic in name      Add Unicode names to test data        QA       done
SHOP-851  Major     Requirement: limit missing  AC with examples at refinement        PO + QA  in progress

Summary: no test 3 | data 2 | environment 3 | requirements 1  (total 9)
🔧 Що в артефакті: Кожен рядок — один escape-дефект із причиною, конкретною дією, власником і статусом. Підсумковий рядок групує причини, і саме він показує, яка зміна процесу дасть найбільший ефект.
🔧 What the artefact shows: Each row is one escaped defect with a cause, a concrete action, an owner and a status. The summary line groups the causes, and that is what shows which process change will have the biggest effect.
🗣 Як читати вголос Коли мене питають, як зменшити leakage, я розповідаю про escape analysis. Кожен баг з продакшну ми розбирали одним питанням: чому ми його не зловили? І часто виявлялося, що справа не в тестах, а в середовищі чи вимогах. Наприклад, у нас третина пропусків була через фіче-флаги, які на стейджингу стояли інакше. Одна синхронізація перед регресією дала більше, ніж десяток нових автотестів.
🗣 How to say it out loud When I'm asked how to reduce leakage, I talk about escape analysis. We went through every production bug with a single question: why didn't we catch it? Quite often the answer wasn't the tests at all but the environment or the requirements. For instance, a third of our misses came from feature flags set differently on staging. One sync step before regression did more than a dozen new automated tests would have.
31 Що таке DRE (defect removal efficiency) і як вона пов'язана з leakage? What is DRE (defect removal efficiency), and how does it relate to leakage?
Метрики дефектівDefect metrics Middle ▶

Що це: DRE — частка дефектів, які команда прибрала до релізу, від усіх дефектів, що зрештою виявились у цій версії. Формула: дефекти, знайдені до релізу, поділені на суму знайдених до і після, помножено на 100. Якщо рахувати з тим самим вікном і тими самими фільтрами, DRE і leakage у сумі дають 100%: це дві сторони одного дробу.

Навіщо і коли: Leakage звучить як «скільки ми пропустили», DRE — як «наскільки ефективно ми ловимо». Для керівництва друга формулювання часто зрозуміліша. Крім того, DRE можна рахувати по фазах: скільки дефектів, наявних на вході в фазу, ця фаза знайшла. Так видно, яка активність — code review, системне тестування, UAT — працює слабше.

Приклад з проєкту: За реліз знайдено 184 дефекти до виходу і 16 після. DRE = 184 / 200 × 100 = 92%, leakage = 8%. По фазах: на вході в системне тестування в коді «сиділо» 150 дефектів (120 знайшло саме тестування, 30 — пізніші фази і продакшн), тож DRE системного тестування — 80%. UAT знайшов 14 із 30, що дійшли до нього, — лише 47%, і ми переглянули сценарії UAT.

Підводні камені: Рахувати DRE одразу після релізу, коли пост-релізних багів ще не встигли знайти: цифра завжди буде ідеальною. Змішувати в одному числі дефекти всіх рівнів важливості.

Уточнююче питання: «Що краще показувати менеджменту — DRE чи leakage?» — одне з двох, не обидва, щоб не виглядало як дві різні новини; і завжди з вікном спостереження.

What it is: DRE is the share of defects the team removed before release out of all defects that eventually turned up in that version. The formula: defects found before release, divided by the sum found before and after, times 100. When calculated with the same window and the same filters, DRE and leakage add up to 100%: they are two sides of one fraction.

Why and when: Leakage reads as "how much we missed", DRE as "how effectively we catch things". For leadership the second phrasing is often easier to take in. DRE can also be calculated per phase: how many of the defects present when a phase started did that phase find. That shows which activity, whether code review, system testing or UAT, is the weak link.

From a project: In one release 184 defects were found before launch and 16 after. DRE = 184 / 200 × 100 = 92%, leakage = 8%. By phase: 150 defects were in the code when system testing started (testing found 120, later phases and production found 30), so system testing DRE was 80%. UAT found 14 of the 30 that reached it, only 47%, and we reworked the UAT scenarios.

Pitfalls: Calculating DRE right after release, before anyone has had time to find post-release bugs: the figure will always look perfect. Mixing every severity level into one number.

Follow-up question: "Which should you show management, DRE or leakage?" One of the two, not both, so it does not read as two separate pieces of news; and always with the observation window.

Формули і розрахунок по фазах
Formulas and per-phase calculation
DRE %      = до релізу / (до релізу + після релізу) x 100
Leakage %  = після релізу / (до релізу + після релізу) x 100

Реліз 5.2 (вікно 30 днів):  184 / (184 + 16) x 100 = 92 %   leakage = 8 %

DRE фази = знайдено у фазі / (знайдено у фазі + знайдено пізніше)
Системне тестування: 120 / (120 + 30) = 80 %
UAT:                  14 / (14 + 16)  = 47 %
DRE %      = pre-release / (pre-release + post-release) x 100
Leakage %  = post-release / (pre-release + post-release) x 100

Release 5.2 (30-day window):  184 / (184 + 16) x 100 = 92 %   leakage = 8 %

Phase DRE = found in phase / (found in phase + found later)
System testing: 120 / (120 + 30) = 80 %
UAT:             14 / (14 + 16)  = 47 %
🔧 Що в артефакті: Дві верхні формули мають однаковий знаменник, тому разом дають 100%. Нижні рядки рахують DRE окремо для кожної фази: «знайдено пізніше» — це все, що пройшло крізь фазу і спливло на наступних етапах або в продакшні.
🔧 What the artefact shows: The two formulas at the top share a denominator, so together they make 100%. The lower lines compute DRE per phase: "found later" is everything that slipped through the phase and surfaced in later stages or in production.
🗣 Як читати вголос Я пояснюю DRE як дзеркало leakage: якщо ми до релізу знайшли дев'яносто два відсотки дефектів, то вісім пропустили. Але найцікавіше — рахувати її по фазах. У нас так з'ясувалося, що UAT ловить менше половини того, що до нього доходить, і проблема була не в людях, а в тому, що сценарії UAT майже повторювали нашу регресію. Тільки важливо чекати з розрахунком хоча б місяць після релізу.
🗣 How to say it out loud I explain DRE as the mirror image of leakage: if we found ninety-two percent of defects before release, we missed eight. The interesting part, though, is calculating it per phase. That's how we discovered our UAT was catching less than half of what reached it, and the issue wasn't the people, it was that the UAT scenarios almost duplicated our regression. The key is waiting at least a month after release before you calculate it.
32 Навіщо відстежувати перевідкриті баги і що показує reopen rate? Why track reopened bugs, and what does the reopen rate tell you?
Метрики дефектівDefect metrics Middle ▶

Що це: Reopen rate — частка багів, які після переходу в Resolved або Done повернули назад, бо перевірка показала, що проблема лишилась. Формула: кількість багів, перевідкритих за період, поділена на кількість виправлених за той самий період, помножено на 100.

Навіщо і коли: Кожне перевідкриття — це ще одне коло «розробник → QA → розробник», тобто прихована вартість і затримка релізу. Метрика показує якість виправлень і якість самих репортів. Типові причини: виправили симптом, а не причину; фікс перевірили лише на одному браузері чи ролі; кроки в репорті неповні, і розробник полагодив інший сценарій; фікс не доїхав у середовище, де його перевіряє QA.

Приклад з проєкту: За спринт закрили 60 багів, 9 з них перевідкрили — reopen rate 15%. Розбір показав, що 5 із 9 стосувались мобільного Safari, на якому розробники не перевіряли. Додали в Definition of Done пункт «перевірено на матриці браузерів з тікета», і за два спринти показник опустився до 6%.

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

Уточнююче питання: «Який reopen rate нормальний?» — універсальної норми немає; важливий тренд і причини, а стабільні 20% і більше — сигнал розібрати процес code review і перевірки фіксу.

What it is: Reopen rate is the share of bugs that moved to Resolved or Done and were then sent back because verification showed the problem was still there. The formula: bugs reopened in a period, divided by bugs fixed in the same period, times 100.

Why and when: Every reopen is another loop of developer to QA to developer, which means hidden cost and a delayed release. The metric reflects the quality of fixes and the quality of the reports themselves. Typical causes: the symptom was fixed rather than the cause; the fix was checked on only one browser or role; the report steps were incomplete, so the developer fixed a different scenario; the fix never reached the environment where QA verifies it.

From a project: In one sprint 60 bugs were closed and 9 of them reopened, a reopen rate of 15%. The breakdown showed that 5 of the 9 involved mobile Safari, which developers were not checking. We added "verified on the browser matrix from the ticket" to the Definition of Done, and within two sprints the figure fell to 6%.

Pitfalls: Reopening a ticket when you have found a new, merely similar bug: that inflates the metric and muddles the history. A new defect gets a new ticket with a link. Another distortion is counting reopens because the fix is not deployed yet; that is a delivery process issue, not a fix quality one.

Follow-up question: "What reopen rate is normal?" There is no universal norm; the trend and the causes matter, and a steady 20% or more is a signal to look at code review and fix verification.

JQL і розрахунок за спринт
JQL and sprint calculation
-- reopened during the last sprint (numerator)
project = SHOP AND issuetype = Bug
  AND status CHANGED FROM "Resolved" TO "Reopened" DURING ("2026-09-14", "2026-09-27")

-- resolved during the same sprint (denominator)
project = SHOP AND issuetype = Bug
  AND status CHANGED TO "Resolved" DURING ("2026-09-14", "2026-09-27")

Reopen rate = 9 / 60 x 100 = 15 %
🔧 Що в артефакті: Обидва запити використовують оператор CHANGED ... DURING з однаковим вікном дат, тож чисельник і знаменник рахуються за той самий спринт. Назви статусів беруться з вашого workflow; якщо він інший, підставте свої.
🔧 What the artefact shows: Both queries use the CHANGED ... DURING operator with the same date window, so numerator and denominator cover the same sprint. Status names come from your workflow; if yours differs, substitute your own.
🗣 Як читати вголос Перевідкриті баги я відстежую, бо кожен з них — це зайве коло між мною і розробником, і релізи через це їдуть. Але сама цифра мені мало що каже, я завжди дивлюсь на причини. Одного разу виявилось, що більшість повернень — через мобільний Safari, на якому ніхто не перевіряв фікс. Один новий пункт у Definition of Done — і показник упав більш ніж удвічі. І я стежу, щоб новий схожий баг заводили окремим тікетом, а не перевідкривали старий.
🗣 How to say it out loud I track reopened bugs because each one is an extra loop between me and the developer, and releases slip because of it. The number itself tells me little, though; I always look at the causes. Once it turned out most of the reopens came from mobile Safari, where nobody was checking the fix. One new line in the Definition of Done and the rate dropped by more than half. I also make sure a new, similar bug gets its own ticket instead of reopening the old one.
33 Яка метрика показує, скільки дефект був відкритим: defect age чи MTTR? Which metric shows how long a defect stays open: defect age or MTTR?
Метрики дефектівDefect metrics Middle ▶

Що це: Тут дві різні метрики. Defect age — скільки часу вже живе відкритий баг: сьогоднішня дата мінус дата створення. Вона про поточний беклог. Time to resolve, яку в командах часто теж називають MTTR (mean time to resolve/repair), рахується для вже закритих: дата вирішення мінус дата створення, усереднена за період. Вона про швидкість реакції.

Навіщо і коли: Age показують у щотижневому звіті як «найстаріші відкриті Critical/Major» і як розподіл за віковими кошиками (до 7 днів, 7–30, понад 30). Time to resolve порівнюють між релізами і між рівнями severity; для Critical часто є SLA, наприклад 2 робочі дні.

Приклад з проєкту: П'ять закритих Major-багів вирішили за 1, 2, 2, 3 і 32 дні. Середнє — 40 / 5 = 8 днів, медіана — 2 дні. Середнє створювало враження, що команда повільна, хоча насправді один баг чекав на сторонню бібліотеку. У звіті ми почали показувати медіану і окремо список «хвостів» понад 30 днів.

Підводні камені: Середнє чутливе до викидів, тому краще медіана або 85-й перцентиль. Час від створення включає очікування triage, тож корисно розбити на «до взяття в роботу» і «в роботі». Не варто змішувати інцидентний MTTR (відновлення сервісу, години) з часом виправлення бага (дні): це різні процеси.

Уточнююче питання: «Що робити з багами віком понад 90 днів?» — переглянути на triage: або запланувати, або чесно закрити як Won't Fix з поясненням, щоб беклог відображав реальні наміри.

What it is: These are two different metrics. Defect age is how long an open bug has been alive: today's date minus the creation date. It is about the current backlog. Time to resolve, which teams often also call MTTR (mean time to resolve/repair), is calculated for closed bugs: resolution date minus creation date, averaged over a period. It is about how fast the team reacts.

Why and when: Age shows up in the weekly report as "oldest open Critical/Major" and as a distribution across age buckets (under 7 days, 7 to 30, over 30). Time to resolve is compared across releases and across severity levels; Critical bugs often have an SLA such as 2 working days.

From a project: Five closed Major bugs were resolved in 1, 2, 2, 3 and 32 days. The mean is 40 / 5 = 8 days, the median is 2 days. The mean made the team look slow, when in fact one bug had been waiting on a third-party library. In the report we switched to the median plus a separate list of "tails" over 30 days.

Pitfalls: The mean is sensitive to outliers, so the median or the 85th percentile works better. Time from creation includes waiting for triage, so it helps to split it into "until picked up" and "in progress". Do not mix incident MTTR (restoring a service, measured in hours) with bug fix time (measured in days): they are different processes.

Follow-up question: "What do you do with bugs older than 90 days?" Review them at triage: either schedule them or honestly close them as Won't Fix with a reason, so the backlog reflects real intentions.

Розрахунок медіани і запити
Median calculation and queries
Time to resolve, 5 Major bugs (days): 1, 2, 2, 3, 32
Mean   = (1 + 2 + 2 + 3 + 32) / 5 = 8 days
Median = 2 days

-- open bugs older than 30 days (defect age), oldest first
project = SHOP AND issuetype = Bug AND statusCategory != Done
  AND created <= -30d ORDER BY created ASC

-- median time to resolve per month (PostgreSQL over a Jira export)
SELECT date_trunc('month', resolved_at) AS month,
       percentile_cont(0.5) WITHIN GROUP (
         ORDER BY EXTRACT(EPOCH FROM resolved_at - created_at) / 86400
       ) AS median_days
FROM bugs
WHERE resolved_at IS NOT NULL AND severity = 'Major'
GROUP BY 1 ORDER BY 1;
🔧 Що в артефакті: Перші рядки показують, як один викид у 32 дні піднімає середнє до 8 днів, тоді як медіана лишається 2. JQL знаходить відкриті баги, старші за 30 днів, а SQL рахує помісячну медіану часу вирішення через percentile_cont по вивантаженій таблиці багів.
🔧 What the artefact shows: The first lines show how one 32-day outlier pushes the mean to 8 days while the median stays at 2. The JQL finds open bugs older than 30 days, and the SQL computes the monthly median time to resolve with percentile_cont over an exported bug table.
🗣 Як читати вголос Я розрізняю дві речі: скільки вже висить відкритий баг і скільки в середньому йде на виправлення закритих. Перше — про беклог, друге — про швидкість команди. І для другого я майже завжди беру медіану, бо один баг, що місяць чекав на стороннього вендора, робить середнє зовсім нечесним. Тому в звіті в мене медіана плюс окремий список найстаріших багів з поясненням, чому вони висять.
🗣 How to say it out loud I separate two things: how long an open bug has been sitting there, and how long closed bugs took to fix on average. The first is about the backlog, the second about the team's speed. For the second I almost always use the median, because one bug that waited a month for an outside vendor makes the mean completely misleading. So my report has the median plus a separate list of the oldest bugs, with a note on why each one is still open.
34 Що таке MTBF і MTTR і як QA їх використовує? What are MTBF and MTTR, and how does QA use them?
Метрики дефектівDefect metrics Senior ▶

Що це: Це метрики надійності з експлуатації. MTBF (mean time between failures) — середній час роботи системи між відмовами: час безвідмовної роботи, поділений на кількість відмов. MTTR (mean time to repair/restore) — середній час від початку збою до відновлення: сумарний час простою, поділений на кількість збоїв. Разом вони дають доступність: MTBF / (MTBF + MTTR).

Навіщо і коли: Ці цифри належать SRE і on-call, але QA впливає на обидві. На MTBF — через регресійні тести на кожен інцидент, навантажувальні й chaos-перевірки, контроль конфігурацій. На MTTR — через швидкі smoke-тести після hotfix і rollback, через чекліст перевірки відновлення і через участь у postmortem, де QA формулює, яким тестом цей збій можна було зловити.

Приклад з проєкту: За 30 днів (720 годин) сервіс оплати мав 4 збої тривалістю 1,5; 0,5; 3 і 1 година, разом 6 годин. MTTR = 6 / 4 = 1,5 години. MTBF = (720 − 6) / 4 = 178,5 години. Доступність = 178,5 / 180 ≈ 99,17% — нижче за SLO 99,5%. Два збої з чотирьох були після релізів, тож ми додали post-deploy smoke на оплату і canary на 5% трафіку.

Підводні камені: Рахувати MTBF у системах, де відмови залежать від навантаження чи релізів, як у залізі з рівномірним зносом: середнє приховує кластери збоїв. Вважати «відмовою» тільки повний даун, ігноруючи деградацію. Приписувати QA метрику, на яку команда не має важелів.

Уточнююче питання: «Що важливіше покращувати — MTBF чи MTTR?» — для частих релізів зазвичай MTTR: швидкий rollback і добрий моніторинг дешевші, ніж спроба виключити всі збої.

What it is: These are operational reliability metrics. MTBF (mean time between failures) is the average time a system runs between failures: uptime divided by the number of failures. MTTR (mean time to repair/restore) is the average time from the start of an outage to recovery: total downtime divided by the number of outages. Together they give availability: MTBF / (MTBF + MTTR).

Why and when: These numbers belong to SRE and on-call, but QA influences both. MTBF through regression tests for every incident, load and chaos checks, and configuration checks. MTTR through fast smoke tests after a hotfix or rollback, a recovery verification checklist and taking part in the postmortem, where QA spells out which test could have caught the failure.

From a project: Over 30 days (720 hours) the payment service had 4 outages lasting 1.5, 0.5, 3 and 1 hours, 6 hours in total. MTTR = 6 / 4 = 1.5 hours. MTBF = (720 − 6) / 4 = 178.5 hours. Availability = 178.5 / 180 ≈ 99.17%, below the 99.5% SLO. Two of the four outages came right after releases, so we added a post-deploy smoke test on payments and a canary on 5% of traffic.

Pitfalls: Treating MTBF for software whose failures depend on load or releases like hardware with even wear: the average hides clusters of failures. Counting only a full outage as a "failure" and ignoring degradation. Assigning QA a metric the team has no levers on.

Follow-up question: "Which matters more to improve, MTBF or MTTR?" With frequent releases usually MTTR: fast rollback and good monitoring are cheaper than trying to rule out every failure.

Розрахунок за 30 днів
30-day calculation
Період: 30 днів = 720 год
Збої (год простою): 1,5 + 0,5 + 3 + 1 = 6 год, кількість = 4

MTTR = 6 / 4               = 1,5 год
MTBF = (720 - 6) / 4       = 178,5 год
Доступність = MTBF / (MTBF + MTTR) = 178,5 / 180 = 99,17 %
Перевірка:   (720 - 6) / 720                     = 99,17 %
SLO 99,5 % -> допустимий простій за 30 днів = 3,6 год  (фактично 6)

# Дії QA після postmortem:
- регресійний тест на кожен інцидент (лінк на incident-тікет)
- post-deploy smoke на оплату, запуск у пайплайні після релізу
Period: 30 days = 720 h
Outages (hours down): 1.5 + 0.5 + 3 + 1 = 6 h, count = 4

MTTR = 6 / 4               = 1.5 h
MTBF = (720 - 6) / 4       = 178.5 h
Availability = MTBF / (MTBF + MTTR) = 178.5 / 180 = 99.17 %
Check:        (720 - 6) / 720                     = 99.17 %
SLO 99.5 % -> allowed downtime in 30 days = 3.6 h  (actual 6)

# QA actions after the postmortem:
- a regression test per incident (linked to the incident ticket)
- post-deploy smoke on payments, run in the pipeline after release
🔧 Що в артефакті: Розрахунок показує зв'язок трьох величин: MTTR і MTBF рахуються з тих самих 4 збоїв, а доступність через формулу збігається з простою перевіркою «час роботи / весь період». Рядок про SLO переводить відсоток у бюджет простою в годинах, що зрозуміліше стейкхолдерам.
🔧 What the artefact shows: The calculation shows how the three values connect: MTTR and MTBF come from the same 4 outages, and availability via the formula matches the simple check of uptime over the whole period. The SLO line turns the percentage into a downtime budget in hours, which stakeholders grasp more easily.
🗣 Як читати вголос MTBF і MTTR — це не суто QA-метрики, і я так і кажу. Але впливати на них ми можемо. Коли я розбирала інциденти платіжного сервісу, виявилося, що половина збоїв стається одразу після релізу. Тож ми додали smoke-перевірку оплати в пайплайн після деплою і canary на невелику частку трафіку. А на кожен postmortem я приходжу з одним питанням: який тест мав це зловити, і чи він уже є в регресії.
🗣 How to say it out loud MTBF and MTTR aren't purely QA metrics, and I say that openly. But we can move them. When I went through the payment service incidents, it turned out half of the outages happened right after a release. So we added a payment smoke check to the pipeline after deploy and a canary on a small slice of traffic. And I go into every postmortem with one question: which test should have caught this, and is it already in the regression suite?
35 Які тікети виключати з метрик дефектів: Duplicate, Not a Bug, Deferred, покращення? Which tickets should be excluded from defect metrics: Duplicate, Not a Bug, Deferred, improvements?
Метрики дефектівDefect metrics Middle ▶

Що це: Не кожен тікет типу Bug є дефектом продукту. Duplicate — той самий баг, заведений удруге; Not a Bug або Won't Do — поведінка відповідає вимогам; Cannot Reproduce — підтвердити не вдалося. Їх прибирають із density, leakage і розподілу за severity. Deferred — інша історія: це справжній дефект, виправлення якого відклали, тож він лишається в метриках і окремо відображається як відомий борг. Improvement request — пропозиція змінити поведінку, яка працює за вимогами; якщо її завели як Bug, вона завищує дефектні цифри.

Навіщо і коли: Правила фільтрації фіксують один раз і пишуть у звіті, інакше дві людини отримають різні числа з тих самих даних. Проміжні статуси на кшталт In Review (фікс на рев'ю коду) не є закриттям: такі баги рахуються відкритими, поки фікс не перевірено.

Приклад з проєкту: За квартал у проєкті було 140 тікетів типу Bug. Після фільтрації: 18 дублікатів, 11 Not a Bug і Won't Do, 9 Cannot Reproduce і 7 покращень, помилково заведених як баги. Лишилось 95 реальних дефектів, і density знизилась на третину. Водночас частка дублікатів 13% стала окремим сигналом: команда погано шукала перед створенням тікета.

Підводні камені: Ховати незручні баги в Deferred, щоб «почистити» звіт. Масово закривати старі баги як Cannot Reproduce без перевірки.

Уточнююче питання: «А чи варто рахувати відхилені тікети взагалі?» — так, окремо: висока частка Not a Bug вказує на неясні вимоги або на те, що QA і PO по-різному їх розуміють.

What it is: Not every ticket of type Bug is a product defect. Duplicate is the same bug filed a second time; Not a Bug or Won't Do means the behaviour matches the requirements; Cannot Reproduce means it could not be confirmed. These are removed from density, leakage and the severity split. Deferred is a different story: it is a real defect whose fix was postponed, so it stays in the metrics and is shown separately as known debt. An improvement request proposes changing behaviour that works as specified; filed as a Bug, it inflates the defect numbers.

Why and when: Filtering rules are agreed once and written into the report, otherwise two people will get different numbers from the same data. Intermediate statuses like In Review (the fix is in code review) are not closure: such bugs count as open until the fix is verified.

From a project: Over a quarter the project had 140 tickets of type Bug. After filtering: 18 duplicates, 11 Not a Bug and Won't Do, 9 Cannot Reproduce and 7 improvements misfiled as bugs. That left 95 real defects, and density dropped by a third. At the same time the 13% duplicate share became a signal of its own: the team was not searching before filing.

Pitfalls: Hiding awkward bugs in Deferred to "clean up" the report. Mass-closing old bugs as Cannot Reproduce without checking.

Follow-up question: "Should rejected tickets be counted at all?" Yes, separately: a high Not a Bug share points to unclear requirements or to QA and the PO reading them differently.

JQL для чистої вибірки і розрахунок
JQL for a clean set and the numbers
-- real defects for the quarter
project = SHOP AND issuetype = Bug
  AND created >= startOfQuarter(-1) AND created <= endOfQuarter(-1)
  AND (resolution is EMPTY
       OR resolution not in (Duplicate, "Won't Do", "Cannot Reproduce"))

-- deferred defects, kept in metrics and listed as known debt
project = SHOP AND issuetype = Bug AND status = Deferred ORDER BY priority DESC

All Bug tickets             140
 - Duplicate                -18
 - Not a Bug / Won't Do     -11
 - Cannot Reproduce          -9
 - improvements, retyped     -7
Real defects                 95
🔧 Що в артефакті: Перший запит бере баги попереднього кварталу і виключає відхилені резолюції; умова resolution is EMPTY потрібна, бо not in не повертає тікети без резолюції. Другий запит показує відкладені дефекти окремо, а таблиця пояснює, звідки різниця між 140 і 95.
🔧 What the artefact shows: The first query takes last quarter's bugs and drops the rejected resolutions; the resolution is EMPTY clause is needed because not in does not return tickets with no resolution. The second query lists deferred defects separately, and the table explains the gap between 140 and 95.
💡 Простими словами: Duplicate і Not a Bug — не дефекти продукту, а Deferred — дефект, просто відкладений.
💡 In plain words: Duplicate and Not a Bug are not product defects; Deferred is a defect, just postponed.
🗣 Як читати вголос Перш ніж показувати будь-яку цифру за багами, я домовляюсь, що саме ми рахуємо. Дублікати, «не баг» і невідтворювані — геть із метрик, а от відкладені лишаються, бо проблема нікуди не ділась. Якось зі ста сорока «багів» після такої чистки лишилось дев'яносто п'ять справжніх. І окремо я показала, що кожен сьомий тікет — дублікат, а це вже була розмова про те, як ми шукаємо перед створенням.
🗣 How to say it out loud Before I show any bug numbers, I agree with the team what exactly we're counting. Duplicates, not-a-bug and can't-reproduce go out of the metrics, but deferred ones stay, because the problem hasn't gone anywhere. Once, out of a hundred and forty "bugs", only ninety-five real ones survived that cleanup. And I showed separately that about one ticket in seven was a duplicate, which turned into a conversation about how we search before filing.
36 Як класифікація root cause перетворює список багів на звіт, з яким можна щось робити? How does root cause classification turn a bug list into an actionable report?
Метрики дефектівDefect metrics Middle ▶

Що це: Root cause — справжня причина дефекту, яку фіксують після виправлення: помилка в логіці коду, неповна чи суперечлива вимога, верстка UI, інтеграція з іншим сервісом, конфігурація середовища, тестові дані. Коли кожен баг має причину зі сталого списку, їх можна згрупувати і побудувати Pareto: відсортувати категорії за частотою і дивитись на кумулятивну частку.

Навіщо і коли: Список зі ста багів нічого не підказує; Pareto показує, які дві-три причини дають більшість проблем, і куди вкладати зусилля. Тут же розводять UI-дефекти і функціональні: UI-дефект — про відображення (зсунута кнопка, обрізаний текст), функціональний — про поведінку (неправильна сума, не зберігся запис). Кнопка, яка виглядає нормально, але не відправляє форму, — функціональний дефект.

Приклад з проєкту: За два релізи класифікували 120 багів: логіка коду — 42 (35%), вимоги — 30 (25%), інтеграції — 18 (15%), верстка — 14, конфігурація — 10, тестові дані — 6. Перші три категорії дали 75%. Замість «більше тестувати» ми запровадили review acceptance criteria з QA і контрактні тести для двох інтеграцій.

Підводні камені: Категорія «Інше» на 40% робить аналіз марним. Root cause не повинен вказувати на людину. Заповнювати причину має той, хто виправляв, а QA перевіряє, чи вона правдоподібна.

Уточнююче питання: «Скільки категорій має бути?» — шість-вісім: менше — зливаються різні причини, більше — люди плутаються і обирають навмання.

What it is: Root cause is the real reason for a defect, recorded after the fix: a logic error in code, an incomplete or contradictory requirement, UI layout, integration with another service, environment configuration, test data. When every bug has a cause from a stable list, you can group them and build a Pareto: sort categories by frequency and look at the cumulative share.

Why and when: A list of a hundred bugs suggests nothing; a Pareto shows which two or three causes produce most of the trouble and where to invest. This is also where UI defects and functional defects are separated: a UI defect is about presentation (a shifted button, clipped text), a functional one is about behaviour (a wrong total, a record not saved). A button that looks fine but does not submit the form is a functional defect.

From a project: Across two releases we classified 120 bugs: code logic 42 (35%), requirements 30 (25%), integrations 18 (15%), layout 14, configuration 10, test data 6. The top three categories made up 75%. Instead of "testing more", we introduced acceptance criteria reviews with QA and contract tests for two integrations.

Pitfalls: An "Other" category at 40% makes the analysis useless. A root cause must never point at a person. The person who fixed the bug should fill in the cause, and QA checks that it is plausible.

Follow-up question: "How many categories should there be?" Six to eight: fewer and different causes merge, more and people get confused and pick at random.

Pareto за root cause, 120 багів
Root cause Pareto, 120 bugs
Причина          Баги   Частка   Кумулятивно
Логіка коду       42    35,0 %    35,0 %
Вимоги            30    25,0 %    60,0 %
Інтеграції        18    15,0 %    75,0 %
Верстка UI        14    11,7 %    86,7 %
Конфігурація      10     8,3 %    95,0 %
Тестові дані       6     5,0 %   100,0 %
Разом            120

# Дії на топ-3 (75 %): unit-тести на правила знижок, review AC з QA,
# контрактні тести для платежів і доставки
Root cause       Bugs   Share    Cumulative
Code logic        42    35.0 %    35.0 %
Requirements      30    25.0 %    60.0 %
Integrations      18    15.0 %    75.0 %
UI layout         14    11.7 %    86.7 %
Configuration     10     8.3 %    95.0 %
Test data          6     5.0 %   100.0 %
Total            120

# Actions on the top 3 (75 %): unit tests for discount rules, AC review with QA,
# contract tests for payments and delivery
🔧 Що в артефакті: Категорії відсортовані за спаданням, а кумулятивний стовпчик показує, де набирається основна маса: три причини з шести дають 75% дефектів. Коментар унизу прив'язує кожну верхню причину до конкретної дії.
🔧 What the artefact shows: Categories are sorted in descending order, and the cumulative column shows where the bulk accumulates: three causes out of six account for 75% of defects. The comment at the bottom ties each top cause to a concrete action.
🗣 Як читати вголос Мені мало сказати, що в релізі сто двадцять багів. Я хочу знати, звідки вони. Коли ми почали фіксувати root cause з короткого списку, Pareto одразу показав, що чверть проблем — від неповних вимог. І це змінило розмову: замість «QA, тестуйте краще» ми почали разом з PO переглядати acceptance criteria ще до розробки. А ще я стежу, щоб категорія «інше» не розросталась, бо тоді весь аналіз нічого не вартий.
🗣 How to say it out loud Saying a release has a hundred and twenty bugs isn't enough for me. I want to know where they come from. Once we started recording root cause from a short list, the Pareto showed straight away that a quarter of the problems came from incomplete requirements. That changed the conversation: instead of "QA, test harder" we began reviewing acceptance criteria with the PO before development. I also keep an eye on the "other" category, because if it grows the whole analysis is worthless.
37 Які цифри з bug triage потрапляють у щотижневий звіт про якість? Which numbers from bug triage go into the weekly quality report?
Метрики дефектівDefect metrics Middle ▶

Що це: Як проводити саму зустріч triage, розписано в гайді «Bug Reporting». Тут — що з неї виносять у звіт. Основні числа: скільки багів надійшло і скільки закрито за тиждень (і різниця між ними), відкритий беклог за severity, баги без власника або без пріоритету, час від створення до рішення на triage і найстаріші відкриті Critical і Major.

Навіщо і коли: Incoming проти closed — найпростіший індикатор тренду: якщо кілька тижнів поспіль надходить більше, ніж закривається, до релізу беклог не встигне зменшитись. Баги без власника — ризик, що їх просто ніхто не робить. Час до triage показує, чи не лежать нові критичні дефекти днями без рішення. Звіт виходить щотижня в один і той самий день, щоб тренд було видно.

Приклад з проєкту: Тиждень 39: надійшло 23, закрито 19, приріст +4 — третій тиждень поспіль. Беклог 49: 1 Critical, 9 Major, 27 Minor, 12 Trivial. П'ять багів без власника, медіана часу до triage 1,5 дня. Замість просто таблиці висновок у звіті був конкретним: при такому темпі до code freeze Major-беклог не опуститься нижче восьми, тож потрібне рішення PO, які з них переносити.

Підводні камені: Звітувати кількістю без severity: 20 закритих Trivial і 2 нові Critical не означають «покращення». Не показувати тренд за кілька тижнів. Дублювати дашборд замість того, щоб дати висновок і запит на рішення.

Уточнююче питання: «Що робити, якщо incoming постійно більший за closed?» — показати прогноз до релізу і запропонувати варіанти: bug-fix спринт, звуження скоупу або перенесення Minor у наступну версію.

What it is: How to run the triage meeting itself is described in the Bug Reporting guide. This card is about what you take from it into the report. The core numbers: how many bugs came in and how many were closed this week (and the difference), the open backlog by severity, bugs with no owner or no priority, time from creation to a triage decision, and the oldest open Critical and Major bugs.

Why and when: Incoming versus closed is the simplest trend indicator: if more comes in than goes out for several weeks running, the backlog will not shrink before the release. Bugs with no owner are a risk that nobody is working on them. Time to triage shows whether new critical defects sit for days without a decision. The report goes out weekly on the same day so the trend is visible.

From a project: Week 39: 23 in, 19 closed, a net +4, for the third week in a row. Backlog of 49: 1 Critical, 9 Major, 27 Minor, 12 Trivial. Five bugs with no owner, median time to triage 1.5 days. Rather than just a table, the report's conclusion was specific: at this pace the Major backlog will not drop below eight by code freeze, so the PO needs to decide which ones to move.

Pitfalls: Reporting counts without severity: 20 closed Trivial bugs and 2 new Critical ones do not mean "improvement". Not showing the trend over several weeks. Duplicating the dashboard instead of giving a conclusion and a request for a decision.

Follow-up question: "What if incoming keeps exceeding closed?" Show a forecast to the release and offer options: a bug-fix sprint, a narrower scope, or moving Minor bugs to the next version.

Блок щотижневого звіту
Weekly report block
Якість: тиждень 39 (22–28 вересня)
Надійшло / закрито:   23 / 19   (+4; тижні 37–39: +3, +5, +4)
Беклог за severity:   Critical 1 | Major 9 | Minor 27 | Trivial 12  = 49
Без власника:         5  (SHOP-901, 903, 907, 912, 915)
Медіана до triage:    1,5 дня (ціль 2 дні)
Найстаріший Critical: SHOP-874, 9 днів, чекає на фікс від вендора

Висновок: при поточному темпі Major-беклог на code freeze (10.10) буде 8+.
Рішення потрібне від PO до 02.10: які Major переносимо в 5.3.
Quality: week 39 (22-28 September)
In / closed:          23 / 19   (+4; weeks 37-39: +3, +5, +4)
Backlog by severity:  Critical 1 | Major 9 | Minor 27 | Trivial 12  = 49
No owner:             5  (SHOP-901, 903, 907, 912, 915)
Median to triage:     1.5 days (target 2 days)
Oldest Critical:      SHOP-874, 9 days, waiting on a vendor fix

Conclusion: at the current pace the Major backlog at code freeze (10.10) will be 8+.
Decision needed from the PO by 02.10: which Major bugs move to 5.3.
🔧 Що в артефакті: Блок читається зверху вниз: тренд надходження, склад беклогу, ризики (баги без власника, старий Critical) і в кінці — висновок з конкретною датою і запитом на рішення. Саме останні два рядки роблять звіт корисним, а не просто копією дашборда.
🔧 What the artefact shows: The block reads top to bottom: the inflow trend, backlog composition, risks (unowned bugs, an old Critical), and finally a conclusion with a specific date and a request for a decision. Those last two lines are what make the report useful rather than a copy of the dashboard.
🗣 Як читати вголос Після triage я не пересилаю таблицю, я пишу короткий блок з висновком. Скільки прийшло і скільки закрили, як виглядає беклог за severity, які баги ні за ким не закріплені. І обов'язково один рядок про те, що з цього випливає: наприклад, що до code freeze ми не встигаємо, і PO треба вирішити, що переносимо. Люди рідко читають цифри, але запит на рішення з датою вони помічають.
🗣 How to say it out loud After triage I don't forward a table, I write a short block with a conclusion. How many came in and how many were closed, what the backlog looks like by severity, which bugs have no owner. And always one line on what it means: for example, that we won't make it by code freeze and the PO needs to decide what moves. People rarely read numbers, but they do notice a request for a decision with a date on it.
▶ 5 12
38 Що таке критерії завершення тестування (exit criteria) і як звітувати щодо них? What are test completion (exit) criteria, and how do you report against them?
Реліз, комунікація і стратегія метрикRelease decisions, communication and metrics strategy Middle ▶

Що це: Критерії завершення тестування (в ISTQB — test completion criteria, у побуті exit criteria) — це заздалегідь погоджені умови, за яких команда вважає тестування етапу достатнім. Їх записують у тест-план ще до початку спринту чи релізу: прохідність критичного шляху, кількість відкритих дефектів за severity, покриття вимог, стан регресу.

Навіщо і коли: Без таких умов фраза «тестування завершено» означає лише «закінчився час». Критерії перетворюють суперечку «готові чи ні» на перевірку фактів: кожен рядок має ціль, фактичне значення і статус. Звітують щодо них у підсумковому тест-звіті та на go/no-go зустрічі.

Приклад з проєкту: Перед релізом мобільного банкінгу 4.2 з п'яти критеріїв чотири були виконані, а покриття вимог склало 91% замість 95%: три історії про експорт виписок не мали жодного тесту. Ми не приховали це в загальному «майже готово», а показали червоний рядок і план — дописати 7 кейсів за день.

Підводні камені: Критерії, які змінюють заднім числом, щоб «вписатися», втрачають сенс. Ще одна пастка — лише кількісні умови без якісних: 100% пройдених тестів нічого не варті, якщо критичний платіжний сценарій взагалі не входив у набір.

Уточнююче питання: «Що робити, якщо один критерій не виконано, а дата релізу фіксована?» — показати розрив, оцінити ризик і винести рішення про виняток на власника релізу, зафіксувавши його письмово.

What it is: Test completion criteria (the ISTQB term; people usually say exit criteria) are conditions agreed in advance under which the team considers testing of a stage sufficient. They go into the test plan before the sprint or release starts: critical-path pass rate, open defects by severity, requirements coverage, regression status.

Why and when: Without them, "testing is done" only means "we ran out of time". Criteria turn the argument about readiness into a check of facts: every line has a target, an actual value and a status. You report against them in the test summary report and at the go/no-go meeting.

From a project: Before mobile banking release 4.2, four of five criteria were met, but requirements coverage was 91% instead of 95%: three statement-export stories had no tests at all. We did not bury that in a general "almost ready"; we showed a red row with a plan to write 7 cases in one day.

Pitfalls: Criteria rewritten after the fact to fit the result lose their meaning. Another trap is purely numeric conditions: a 100% pass rate is worthless if the critical payment flow was never in the suite.

Follow-up question: "What if one criterion is not met and the release date is fixed?" — show the gap, estimate the risk and let the release owner decide on an exception, recorded in writing.

Звіт щодо критеріїв завершення
Reporting against exit criteria
Критерій                                 | Ціль        | Факт        | Статус
-----------------------------------------|-------------|-------------|-------
Критичний шлях (smoke + оплата) пройдено | 100%        | 100% (48/48)| OK
Відкриті Blocker / Critical              | 0           | 0           | OK
Покриття вимог (RTM)                     | >= 95%      | 91% (62/68) | НІ
Відкриті Major з workaround              | <= 5        | 3           | OK
Регрес виконано на build 4.2.0-rc3       | 100%        | 100%        | OK

Висновок: 4 з 5 критеріїв виконано. Розрив: 6 вимог без тестів
(експорт виписок). План: 7 кейсів до 14.10, повторна оцінка 15.10.
Criterion                                | Target      | Actual      | Status
-----------------------------------------|-------------|-------------|-------
Critical path (smoke + payment) passed   | 100%        | 100% (48/48)| OK
Open Blocker / Critical                  | 0           | 0           | OK
Requirements coverage (RTM)              | >= 95%      | 91% (62/68) | NO
Open Major with workaround               | <= 5        | 3           | OK
Regression run on build 4.2.0-rc3        | 100%        | 100%        | OK

Conclusion: 4 of 5 criteria met. Gap: 6 requirements without tests
(statement export). Plan: 7 cases by 14 Oct, reassess on 15 Oct.
🔧 Що в артефакті: Таблиця «ціль — факт — статус» робить кожен критерій перевірюваним, а у дужках видно абсолютні числа, з яких пораховано відсоток. Висновок під таблицею називає розрив і конкретний план, а не просто колір.
🔧 What the artefact shows: The target / actual / status table makes every criterion checkable, and the brackets show the absolute numbers behind each percentage. The conclusion names the gap and a concrete plan instead of just a colour.
💡 Простими словами: Домовся про правила фінішу до початку гри, а не на фініші.
💡 In plain words: Agree on the finish line before the race starts, not while crossing it.
🗣 Як читати вголос Я завжди прошу зафіксувати критерії завершення в тест-плані ще до початку релізу, інакше потім сперечаються про відчуття. У звіті я показую кожен критерій окремим рядком: що обіцяли, що вийшло, і чи виконано. Якщо щось червоне, я не малюю його жовтим — я пояснюю, чому так, чим це загрожує і скільки часу треба, щоб закрити розрив. А вирішувати, чи йти з винятком, має власник релізу.
🗣 How to say it out loud I always ask for the exit criteria to be written into the test plan before the release starts, otherwise we end up arguing about feelings. In the report each criterion gets its own row: what we promised, what we got, and whether it is met. If something is red I don't paint it yellow; I explain why, what the risk is, and how long it takes to close the gap. Whether to ship with an exception is the release owner's call.
39 Що таке known issue і як описати його у звіті чи release notes? What is a known issue, and how do you document it in a report or release notes?
Реліз, комунікація і стратегія метрикRelease decisions, communication and metrics strategy Junior ▶

Що це: Known issue — відомий дефект, з яким команда свідомо випускає реліз: його знайшли, оцінили і вирішили виправити пізніше. На відміну від просто відкритого бага, known issue має погоджене рішення «випускаємо так» і опис для тих, кого він зачепить.

Навіщо і коли: Список відомих проблем захищає підтримку і клієнтів від сюрпризів: саппорт не ескалює те, що вже відомо, а користувач бачить обхідний шлях. Для кожного пункту потрібні вплив, хто постраждає, workaround і орієнтовна дата виправлення. Саме workaround часто вирішує, чи припустимо випускати з цим дефектом.

Приклад з проєкту: У релізі CRM 7.3 імпорт CSV понад 50 тисяч рядків падав за тайм-аутом. Таких клієнтів було 4 з 1200, і обхід існував — розбити файл на частини. Ми внесли дефект у known issues з кроками обходу, а саппорт отримав готову відповідь для цих чотирьох акаунтів.

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

Уточнююче питання: «Чим known issue відрізняється від Deferred-бага?» — Deferred описує статус у трекері, а known issue — це публічне зобов'язання повідомити про проблему тих, кого вона стосується.

What it is: A known issue is a defect the team knowingly ships with: it was found, assessed and scheduled for a later fix. Unlike an ordinary open bug, it carries an agreed decision to release anyway and a description for the people it will affect.

Why and when: A known-issues list protects support and customers from surprises: support does not escalate what is already known, and users see a way around it. Each entry needs the impact, who is affected, a workaround and an estimated fix date. The workaround is often what decides whether shipping with the defect is acceptable at all.

From a project: In CRM release 7.3, CSV import of more than 50,000 rows failed with a timeout. Only 4 of 1,200 customers had files that large, and a workaround existed: split the file. We listed it as a known issue with the workaround steps, and support got a ready answer for those four accounts.

Pitfalls: One wording for every audience is a mistake. The internal report carries the ticket ID, cause and risk; the customer text describes the symptom in plain language, with no service names and no promises the team may not keep. Another trap is a known issue that rolls over from release to release for years and nobody reviews it.

Follow-up question: "How is a known issue different from a Deferred bug?" — Deferred is a tracker status, while a known issue is a commitment to tell the affected people about the problem.

Known issues: внутрішня і клієнтська версії
Known issues: internal and customer-facing versions
ВНУТРІШНІЙ ЗВІТ (реліз CRM 7.3)
ID        | Вплив                          | Кого зачіпає      | Workaround              | Виправлення
----------|--------------------------------|-------------------|-------------------------|------------
CRM-4412  | Імпорт CSV > 50k рядків падає  | 4 з 1200 клієнтів | Ділити файл по 40k      | 7.4 (28.10)
          | по тайм-ауту 60 с              | (список у тікеті) | рядків                  |
CRM-4398  | Звіт PDF: кирилиця в колонтит. | Усі з укр. локал. | Експорт у XLSX          | 7.3.1

RELEASE NOTES ДЛЯ КЛІЄНТІВ
Відомі проблеми:
- Імпорт дуже великих файлів CSV (понад 50 000 рядків) може
  перерватися. Поки що розділіть файл на кілька менших.
- У PDF-звітах назви в колонтитулах можуть відображатися
  некоректно. Скористайтеся експортом у Excel.
INTERNAL REPORT (CRM release 7.3)
ID        | Impact                         | Who is affected   | Workaround              | Fix
----------|--------------------------------|-------------------|-------------------------|------------
CRM-4412  | CSV import > 50k rows fails    | 4 of 1200 tenants | Split file into 40k     | 7.4 (28 Oct)
          | with 60 s timeout              | (list in ticket)  | row chunks              |
CRM-4398  | PDF report: Cyrillic in footer | All uk-locale     | Export to XLSX          | 7.3.1

CUSTOMER RELEASE NOTES
Known issues:
- Importing very large CSV files (over 50,000 rows) may stop
  before it finishes. For now, split the file into smaller ones.
- In PDF reports, footer titles may not display correctly.
  Use the Excel export instead.
🔧 Що в артефакті: Той самий дефект описано двічі: внутрішня таблиця дає ID, масштаб впливу і дату виправлення, а клієнтський текст лише симптом і обхід. Колонка «Кого зачіпає» з числами показує, чому рішення випускати було прийнятним.
🔧 What the artefact shows: The same defects appear twice: the internal table gives the ID, scale of impact and fix date, while the customer text keeps only the symptom and the workaround. The numeric "who is affected" column shows why shipping was acceptable.
🗣 Як читати вголос Для мене known issue — це не просто відкритий баг, а свідоме рішення випускати з ним, і тоді ми зобов'язані чесно про нього попередити. Я пишу дві версії: для команди з тікетом і ризиком, і для клієнтів простими словами з обхідним шляхом. Найважливіше поле для мене — workaround: якщо обходу немає, а проблема зачіпає багатьох, це вже аргумент не випускати. І я раз на квартал переглядаю старі known issues, щоб вони не жили вічно.
🗣 How to say it out loud To me a known issue is not just an open bug, it's a conscious decision to ship with it, which means we owe people an honest warning. I write two versions: one for the team with the ticket and the risk, and one for customers in plain words with the workaround. The field I care about most is the workaround: if there's no way around it and many users are hit, that's already an argument not to ship. And once a quarter I review old known issues so they don't live forever.
40 Як виглядають release notes з погляду QA? What does a release note look like from QA's side?
Реліз, комунікація і стратегія метрикRelease decisions, communication and metrics strategy Junior ▶

Що це: Release notes — документ, який супроводжує версію: що нового, що виправлено, які відомі проблеми лишилися, іноді що змінилося для інтеграцій. Пишуть його зазвичай продакт або техрайтер, але QA відповідає за точність двох списків: «виправлено» і «відомі проблеми».

Навіщо і коли: Якщо в нотатках написано «виправлено», а баг досі відтворюється, це найшвидший спосіб втратити довіру клієнтів. Тому QA звіряє кожен пункт з тікетами у статусі Verified на конкретному білді, додає в внутрішню версію обсяг тестування і середовища, де перевіряли, і позначає зміни, що ламають зворотну сумісність.

Приклад з проєкту: Для API платіжного шлюзу 3.8 у чернетці було 12 «виправлених» пунктів. Під час звірки з Jira виявилось, що два з них злиті в main, але не потрапили в реліз-гілку. Ми прибрали їх зі списку до публікації, а один перенесли в known issues, бо клієнт на нього вже скаржився.

Підводні камені: Генерувати нотатки автоматично з назв комітів — погана ідея без ручної редакції: «fix NPE in mapper» нічого не каже клієнту. Також часто забувають зміни поведінки, які формально не є багами, але зламають чиюсь інтеграцію, наприклад нове обмеження довжини поля.

Уточнююче питання: «Як перевірити, що список виправлень відповідає білду?» — JQL по fixVersion і статусу плюс порівняння з git log між тегами релізів.

What it is: Release notes accompany a version: what is new, what was fixed, which known issues remain, and sometimes what changed for integrations. A product manager or technical writer usually writes them, but QA owns the accuracy of two lists: "fixed" and "known issues".

Why and when: Listing something as fixed while the bug still reproduces is the fastest way to lose customer trust. So QA checks every entry against tickets in Verified status on a specific build, adds the test scope and environments to the internal version, and flags changes that break backward compatibility.

From a project: For payment gateway API 3.8 the draft listed 12 fixed items. Cross-checking with Jira showed two of them were merged to main but never reached the release branch. We removed them before publishing, and moved one into known issues because a customer was already complaining about it.

Pitfalls: Generating notes straight from commit titles without editing is a bad idea: "fix NPE in mapper" tells a customer nothing. Teams also forget behaviour changes that are not formally bugs but will break someone's integration, such as a new length limit on a field.

Follow-up question: "How do you check the fixed list matches the build?" — a JQL query by fixVersion and status, plus a comparison with git log between release tags.

Release notes з блоком від QA
Release notes with a QA block
Payment Gateway API 3.8.0 (build 3.8.0+412, 07.10)

Нове
- Повернення коштів частинами: POST /v2/refunds приймає amount.

Виправлено (перевірено QA на 3.8.0+412)
- PAY-2210 Дублікат webhook при повторі запиту з тим самим Idempotency-Key.
- PAY-2231 Невірне округлення комісії для JPY (валюта без копійок).

Зміни поведінки
- Поле description обмежене 255 символами, довші запити -> 422.

Відомі проблеми
- PAY-2240 Звіт settlement за день може з'явитися із затримкою до 2 год.

Для внутрішнього використання
Обсяг: регрес 1 340 API-тестів, 96 e2e; середовища staging-eu, staging-us.
Перевірка списку: fixVersion = 3.8.0 AND status = Verified -> 11 тікетів.
Payment Gateway API 3.8.0 (build 3.8.0+412, 7 Oct)

New
- Partial refunds: POST /v2/refunds now accepts amount.

Fixed (verified by QA on 3.8.0+412)
- PAY-2210 Duplicate webhook when a request is retried with the same Idempotency-Key.
- PAY-2231 Wrong fee rounding for JPY (currency without minor units).

Behaviour changes
- The description field is limited to 255 characters; longer requests -> 422.

Known issues
- PAY-2240 The daily settlement report may be delayed by up to 2 hours.

Internal only
Scope: regression of 1,340 API tests, 96 e2e; environments staging-eu, staging-us.
List check: fixVersion = 3.8.0 AND status = Verified -> 11 tickets.
🔧 Що в артефакті: Кожен пункт «виправлено» прив'язаний до тікета і до конкретного білду, а окремий блок «зміни поведінки» попереджає інтеграторів. Внутрішній хвіст фіксує обсяг тестування і запит, яким звірено список.
🔧 What the artefact shows: Every fixed item is tied to a ticket and a specific build, and the separate behaviour-changes block warns integrators. The internal footer records the test scope and the query used to reconcile the list.
🗣 Як читати вголос Release notes зазвичай пише не QA, але я вважаю себе відповідальним за два списки — що виправлено і що лишилося відомою проблемою. Перед публікацією я звіряю кожен пункт з тікетами в статусі Verified на тому самому білді, що йде в прод. Кілька разів це рятувало від ганебної ситуації, коли фікс злили в main, але він не потрапив у реліз. І я окремо дивлюсь на зміни поведінки, бо саме вони ламають інтеграції клієнтів.
🗣 How to say it out loud QA usually doesn't write the release notes, but I consider myself responsible for two lists: what was fixed and what stays a known issue. Before publishing I check each item against Verified tickets on the exact build going to production. A few times that saved us from an embarrassing moment where a fix was merged to main but never made it into the release. I also look separately at behaviour changes, because those are what break customer integrations.
41 Як QA має комунікувати рекомендацію go/no-go? How should QA communicate a go/no-go recommendation?
Реліз, комунікація і стратегія метрикRelease decisions, communication and metrics strategy Senior ▶

Що це: Go/no-go — коротка зустріч або письмове рішення перед релізом, де власник продукту чи релізу вирішує, випускати версію чи ні. Роль QA тут — дати рекомендацію з доказами, а не оголосити вердикт: рішення про бізнес-ризик приймає той, хто за нього відповідає.

Як я формулюю: Рекомендація має одну з трьох форм: GO, GO з умовами або NO-GO. Далі — докази (критерії завершення, відкриті дефекти, що не перевірено), залишковий ризик людською мовою і умови, за яких рекомендація зміниться. Все це вміщається в одне повідомлення, яке можна прочитати за хвилину.

Приклад з проєкту: Перед релізом нового онбордингу я рекомендував «GO з умовами»: усі критерії виконано, крім одного Major-бага в Safari 16 — кнопка «Далі» ховається за клавіатурою на iPhone SE. Умови: реліз за фіче-флагом на 10% трафіку, алерт на конверсію онбордингу, фікс у хотфіксі за 2 дні. Продакт погодився, через 3 дні флаг підняли до 100%.

Підводні камені: QA, що вважає себе «воротарем» і блокує реліз одноосібно, швидко втрачає вплив. Протилежна крайність — розмите «загалом усе ок», яке перекладає відповідальність на QA, якщо щось впаде. Також не можна мовчати про те, що не тестувалося: непокриті зони — теж частина ризику.

Уточнююче питання: «Що, як продакт вирішив GO всупереч вашій NO-GO?» — зафіксувати рішення і прийнятий ризик письмово, домовитись про моніторинг і план відкату, і далі працювати на успіх релізу.

What it is: Go/no-go is a short meeting or written decision before a release where the product or release owner decides whether to ship. QA's role is to give a recommendation backed by evidence, not to announce a verdict: the business-risk decision belongs to whoever is accountable for it.

How I phrase it: The recommendation takes one of three forms: GO, GO with conditions, or NO-GO. Then comes the evidence (exit criteria, open defects, what was not tested), the residual risk in plain language, and the conditions under which the recommendation would change. It all fits in one message you can read in a minute.

From a project: Before shipping a new onboarding flow I recommended "GO with conditions": every criterion was met except one Major bug in Safari 16, where the Next button hid behind the keyboard on iPhone SE. Conditions: release behind a feature flag at 10% of traffic, an alert on onboarding conversion, and a hotfix within 2 days. The product owner agreed, and three days later the flag went to 100%.

Pitfalls: A QA who acts as a gatekeeper and blocks releases alone quickly loses influence. The opposite extreme is a vague "mostly fine", which quietly moves accountability onto QA if something breaks. And never stay silent about what was not tested: uncovered areas are part of the risk too.

Follow-up question: "What if the product owner says GO against your NO-GO?" — record the decision and the accepted risk in writing, agree on monitoring and a rollback plan, then work to make the release succeed.

Чек-лист і повідомлення з рекомендацією
Checklist and recommendation message
ЧЕК-ЛИСТ GO/NO-GO (онбординг 2.0, build 5.12.0-rc2)
[x] Критерії завершення: 5 з 5, крім Major ONB-318 (див. нижче)
[x] Smoke на проді-дзеркалі пройшов
[x] Міграції БД мають скрипт відкату, перевірено на staging
[x] Моніторинг: дашборд конверсії онбордингу + алерт < 35%
[ ] Не перевірено: Android 9 (немає пристрою у фермі) - ризик низький, 1.2% аудиторії
[x] План відкату: вимкнути флаг onboarding_v2, час < 5 хв

РЕКОМЕНДАЦІЯ QA: GO з умовами
Докази: регрес 612/612, 0 Blocker/Critical, 1 Major.
Ризик: ONB-318 - у Safari 16 на малих екранах кнопка «Далі» під
клавіатурою; стосується ~3% нових користувачів, обхід - закрити клавіатуру.
Умови: флаг на 10% трафіку; фікс ONB-318 у хотфіксі до 16.10.
Рекомендація зміниться на NO-GO, якщо конверсія впаде нижче 35%.
Рішення: @product-owner (Олена), дедлайн 15.10 12:00.
GO/NO-GO CHECKLIST (onboarding 2.0, build 5.12.0-rc2)
[x] Exit criteria: 5 of 5, except Major ONB-318 (see below)
[x] Smoke on the production mirror passed
[x] DB migrations have a rollback script, verified on staging
[x] Monitoring: onboarding conversion dashboard + alert at < 35%
[ ] Not tested: Android 9 (no device in the farm) - low risk, 1.2% of users
[x] Rollback plan: switch off flag onboarding_v2, under 5 min

QA RECOMMENDATION: GO with conditions
Evidence: regression 612/612, 0 Blocker/Critical, 1 Major.
Risk: ONB-318 - in Safari 16 on small screens the Next button sits under
the keyboard; affects ~3% of new users, workaround - dismiss the keyboard.
Conditions: flag at 10% of traffic; ONB-318 fixed in a hotfix by 16 Oct.
The recommendation turns into NO-GO if conversion drops below 35%.
Decision: @product-owner (Olena), deadline 15 Oct 12:00.
🔧 Що в артефакті: Чек-лист показує і виконане, і чесно непровірене (Android 9) з оцінкою ризику. Повідомлення відокремлює рекомендацію, докази, ризик, умови і того, хто ухвалює рішення, а також називає поріг, за якого рекомендація зміниться.
🔧 What the artefact shows: The checklist shows what was done and, honestly, what was not (Android 9) with a risk estimate. The message separates recommendation, evidence, risk, conditions and the decision owner, and names the threshold that would change the recommendation.
🗣 Як читати вголос На go/no-go я не кажу «я дозволяю» чи «я забороняю» — я даю рекомендацію, а рішення ухвалює власник релізу. Моє повідомлення завжди має одну з трьох позицій: так, так з умовами, або ні. Під нею — докази, чесний список того, що ми не перевіряли, і ризик у термінах користувачів. І я одразу кажу, що змінить мою думку. Якщо продакт вирішує інакше, я прошу записати це рішення і домовляюсь про моніторинг і відкат.
🗣 How to say it out loud At go/no-go I don't say "I allow it" or "I forbid it". I give a recommendation, and the release owner makes the call. My message always takes one of three positions: yes, yes with conditions, or no. Under it sit the evidence, an honest list of what we didn't test, and the risk described in terms of users. I also say upfront what would change my mind. If the product owner decides differently, I ask for the decision in writing and agree on monitoring and a rollback.
42 Як згадати security-дефект у статус-звіті, не розкривши його? How do you mention a security defect in status reports without exposing it?
Реліз, комунікація і стратегія метрикRelease decisions, communication and metrics strategy Senior ▶

Що це: Вразливість — дефект, опис якого сам по собі є ризиком: детальні кроки в широкому звіті фактично стають інструкцією для атаки. Тому security-баги ведуть в окремому тікеті з обмеженим доступом (security level у Jira або приватний advisory у GitHub), а в загальних звітах згадують лише факт, серйозність і статус.

Навіщо і коли: Статус-звіт читають десятки людей, його пересилають підрядникам і вставляють у презентації. При цьому менеджмент має знати, що реліз затримується через критичну вразливість. Рішення — дозований рівень деталей: для широкої аудиторії — ID, оцінка CVSS, компонент загального рівня і ETA; для команди з допуском — повний опис.

Приклад з проєкту: Під час тестування ми знайшли IDOR: змінивши id у запиті до API рахунків, можна було побачити чужий рахунок. У щотижневому звіті з'явився рядок «SEC-117, High (CVSS 6.5, підвищено через фінансові дані), білінг, фікс у 2.9.1, деталі в закритому тікеті». Розширений опис бачили лише 6 людей з групи безпеки, а публічний запис у changelog з'явився після розгортання фіксу.

Підводні камені: Часто витік відбувається через вкладення: HAR-файл з токенами або скріншот з URL у загальнодоступному тікеті. Також небезпечно дублювати security-баг звичайним тікетом «щоб розробник швидше побачив» — ембарго тоді зникає.

Уточнююче питання: «Коли можна розкрити деталі?» — після того, як фікс розгорнуто всюди і, за потреби, клієнти оновились, згідно з політикою відповідального розкриття компанії.

What it is: A vulnerability is a defect whose description is itself a risk: detailed steps in a widely read report effectively become attack instructions. So security bugs live in a separate ticket with restricted access (a Jira security level or a private GitHub advisory), and general reports mention only the fact, the severity and the status.

Why and when: A status report is read by dozens of people, forwarded to contractors and pasted into slides. At the same time management must know the release is delayed by a critical vulnerability. The answer is graded detail: for the wide audience, the ID, CVSS score, a high-level component and ETA; for the cleared team, the full description.

From a project: During testing we found an IDOR: changing the id in a request to the invoices API exposed another customer's invoice. The weekly report got one line: "SEC-117, High (CVSS 6.5, raised for financial data), billing, fix in 2.9.1, details in a restricted ticket". Only 6 people in the security group saw the full write-up, and the public changelog entry appeared after the fix was deployed.

Pitfalls: Leaks often come through attachments: a HAR file with tokens or a screenshot showing the URL in a publicly visible ticket. Duplicating the security bug as a normal ticket "so the developer sees it faster" is also dangerous, because the embargo is then gone.

Follow-up question: "When can details be disclosed?" — once the fix is deployed everywhere and, if needed, customers have upgraded, following the company's responsible disclosure policy.

Три рівні деталізації одного дефекту
Three levels of detail for the same defect
1) ЩОТИЖНЕВИЙ СТАТУС (усі стейкхолдери)
   Безпека: 1 відкрита вразливість High (SEC-117, CVSS 6.5),
   модуль «Білінг». Фікс у 2.9.1, ETA 18.10. Реліз 2.9.0 не блокує
   за умови, що ендпоінт вимкнено флагом. Деталі - закритий тікет.

2) ЗАКРИТИЙ ТІКЕТ SEC-117 (Jira security level: Security Team)
   Тип: IDOR. GET /api/v1/invoices/{id} не перевіряє, що рахунок
   належить поточному tenant. Кроки, запит, два тестові акаунти.
   CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N -> 6.5 базова,
   підвищено до High через фінансові дані (рішення security lead).

3) CHANGELOG ПІСЛЯ ФІКСУ
   2.9.1: Посилено перевірку доступу до рахунків.
1) WEEKLY STATUS (all stakeholders)
   Security: 1 open High vulnerability (SEC-117, CVSS 6.5),
   Billing module. Fix in 2.9.1, ETA 18 Oct. Does not block 2.9.0
   provided the endpoint stays disabled by a flag. Details: restricted ticket.

2) RESTRICTED TICKET SEC-117 (Jira security level: Security Team)
   Type: IDOR. GET /api/v1/invoices/{id} does not check that the invoice
   belongs to the current tenant. Steps, request, two test accounts.
   CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N -> 6.5 base,
   raised to High because of financial data (security lead decision).

3) CHANGELOG AFTER THE FIX
   2.9.1: Strengthened access checks for invoices.
🔧 Що в артефакті: Той самий дефект описано для трьох аудиторій: широкий звіт дає лише ID, рівень, модуль і ETA, закритий тікет містить технічну суть і вектор CVSS, а changelog з'являється тільки після фіксу. Вектор CVSS пояснює, звідки взялась оцінка, і що її підвищення — окреме задокументоване рішення.
🔧 What the artefact shows: The same defect is written for three audiences: the wide report gives only the ID, level, module and ETA, the restricted ticket holds the technical substance and CVSS vector, and the changelog appears only after the fix. The CVSS vector shows where the score came from, and raising it is a separate, documented decision.
🗣 Як читати вголос Коли я знаходжу вразливість, перше, що я роблю, — заводжу тікет з обмеженим доступом, а не звичайний баг. У загальному статусі я пишу лише ID, рівень серйозності, модуль і коли чекати фікс, бо менеджменту цього достатньо для рішень. Технічні деталі бачать лише люди з допуском. І я особливо уважний до вкладень: один HAR-файл з токеном у відкритому тікеті може зробити більше шкоди, ніж сама вразливість.
🗣 How to say it out loud When I find a vulnerability, the first thing I do is open a restricted ticket instead of a normal bug. In the general status I write only the ID, severity, module and when to expect a fix, because that's enough for management to make decisions. Technical details are visible only to people with clearance. I'm especially careful with attachments: one HAR file with a token in an open ticket can do more damage than the vulnerability itself.
43 Як по-різному говорити про якість з менеджментом і з розробниками? How do you communicate quality differently to management and to developers?
Реліз, комунікація і стратегія метрикRelease decisions, communication and metrics strategy Middle ▶

Що це: Одна й та сама новина про якість має різних читачів з різними питаннями. Менеджер питає: чи встигаємо, що це означає для клієнтів і що мені вирішити. Розробник питає: що саме впало, де, як відтворити і чи це моє. Хороший QA пише дві версії, а не одну «для всіх».

Навіщо і коли: Повідомлення з назвами тестів і стек-трейсами менеджер не дочитає і пропустить ризик. А фраза «оплата під загрозою» без посилань змусить розробника витратити пів години на пошук. Для менеджменту — три рядки: вплив на бізнес, ризик для дати, потрібне рішення. Для розробників — конкретні тести, асерт, що впав, посилання на trace і коміт, з якого почалось.

Приклад з проєкту: Після мержу оновлення бібліотеки валют впали 14 тестів кошика. Менеджер отримав: «Оплата в EUR на staging зламана з учорашнього вечора; реліз у четвер під ризиком, якщо не виправимо до середи; рішення від вас не потрібне, команда вже працює». Розробники отримали список тестів, асерт з очікуваною і фактичною сумою та посилання на Playwright trace.

Підводні камені: Жаргон у повідомленні для менеджменту («flaky», «selector», «rc3») і розмиті формулювання для розробників. Інша помилка — надсилати менеджеру проміжні тривоги, які розвіються за годину: краще коротко підтвердити, коли причину вже зрозуміло.

Уточнююче питання: «Як змусити менеджмент читати ваші звіти?» — починати з висновку і дії, а деталі давати посиланням.

What it is: The same piece of quality news has readers with different questions. A manager asks: are we on track, what does it mean for customers, what do I need to decide. A developer asks: what exactly failed, where, how to reproduce it, and is it mine. A good QA writes two versions instead of one "for everyone".

Why and when: A message full of test names and stack traces will not be finished by a manager, and the risk gets missed. A line like "payments at risk" with no links makes a developer spend half an hour searching. For management: three lines covering business impact, risk to the date, and any decision needed. For developers: the specific tests, the failed assertion, a trace link and the commit where it started.

From a project: After a currency library upgrade was merged, 14 cart tests failed. The manager got: "EUR payments on staging are broken since last night; Thursday's release is at risk unless fixed by Wednesday; no decision needed from you, the team is on it." Developers got the list of tests, the assertion with expected and actual totals, and a link to the Playwright trace.

Pitfalls: Jargon in the management message ("flaky", "selector", "rc3") and vague wording for developers. Another mistake is sending managers interim alarms that disappear an hour later: it is better to send a short confirmation once the cause is understood.

Follow-up question: "How do you get management to read your reports?" — lead with the conclusion and the action, and give details as links.

Одна новина — два повідомлення
One piece of news, two messages
ДЛЯ МЕНЕДЖМЕНТУ (#release-status)
Оплата в EUR на staging не працює з 21:40 учора.
Ризик: реліз у четвер зсунеться, якщо фікс не буде до середи 18:00.
Від вас рішень поки не треба; наступне оновлення - сьогодні о 15:00.

ДЛЯ РОЗРОБНИКІВ (#team-checkout)
Після мержу PR #2291 (currency-lib 4.0 -> 5.1) падають 14 тестів
у cart/checkout.spec.ts, наприклад:
  checkout > applies VAT for EUR cart
  expect(total).toHaveText('119,00 EUR')
  Received: '11 900,00 EUR'   (схоже, сума вже в центах)
Trace: https://ci.example.com/runs/48213/trace.zip
Відтворення локально: npx playwright test cart/checkout.spec.ts -g EUR
FOR MANAGEMENT (#release-status)
EUR payments on staging have been broken since 21:40 yesterday.
Risk: Thursday's release slips if the fix is not in by Wednesday 18:00.
No decisions needed from you yet; next update today at 15:00.

FOR DEVELOPERS (#team-checkout)
Since PR #2291 was merged (currency-lib 4.0 -> 5.1), 14 tests fail
in cart/checkout.spec.ts, for example:
  checkout > applies VAT for EUR cart
  expect(total).toHaveText('119,00 EUR')
  Received: '11 900,00 EUR'   (looks like the amount is already in cents)
Trace: https://ci.example.com/runs/48213/trace.zip
Reproduce locally: npx playwright test cart/checkout.spec.ts -g EUR
🔧 Що в артефакті: Повідомлення для менеджменту відповідає на «що, чим загрожує, що від мене треба» і обіцяє час наступного оновлення. Повідомлення для розробників дає PR-підозрюваний, назву тесту, асерт з фактичним значенням, trace і команду для локального відтворення.
🔧 What the artefact shows: The management message answers "what, what's at stake, what do you need from me" and promises the time of the next update. The developer message gives the suspect PR, the test name, the assertion with the actual value, the trace and a command to reproduce locally.
💡 Простими словами: Менеджеру — висновок і наслідки, розробнику — адреса проблеми.
💡 In plain words: Give the manager the conclusion and consequences; give the developer the address of the problem.
🗣 Як читати вголос Я пишу одну й ту саму новину двічі. Менеджеру — три рядки без жаргону: що зламалося з погляду клієнта, чим це загрожує даті і чи треба від нього якесь рішення. Розробникам — назви тестів, що саме очікувалось і що прийшло, посилання на trace і підозрілий PR. Так менеджер не тоне в деталях, а розробник не витрачає пів години, щоб знайти, де дивитися.
🗣 How to say it out loud I write the same news twice. The manager gets three lines with no jargon: what broke from the customer's point of view, what it means for the date, and whether they need to decide anything. Developers get the test names, what was expected and what came back, the trace link and the suspect PR. That way the manager doesn't drown in details, and the developer doesn't spend half an hour working out where to look.
44 Що таке DORA-метрики і де в них місце QA? What are DORA metrics, and where does QA fit in?
Реліз, комунікація і стратегія метрикRelease decisions, communication and metrics strategy Senior ▶

Що це: DORA-метрики — чотири показники ефективності доставки ПЗ з досліджень DevOps Research and Assessment: частота деплоїв, lead time for changes (від коміту до проду), change failure rate (частка деплоїв, що спричинили збій) і час відновлення після збою; у новіших звітах його називають failed deployment recovery time. Перші дві описують швидкість, останні дві — стабільність.

Де тут QA: Жодна з метрик не називається «тестування», але QA впливає на всі. Повільний або нестабільний пайплайн тестів подовжує lead time і зменшує частоту деплоїв. Прогалини в регресі підвищують change failure rate. Швидкі smoke-перевірки після деплою і хороші алерти скорочують час відновлення.

Приклад з проєкту: За квартал команда зробила 120 деплоїв, 9 з них вимагали хотфіксу або відкату: CFR = 9 / 120 = 7,5%. Розбір показав, що 6 із 9 збоїв стосувались міграцій БД, які не перевірялись на копії продових даних. Ми додали в пайплайн прогін міграцій на анонімізованому знімку, і наступного кварталу було 4 збої на 140 деплоїв, тобто близько 2,9%.

Підводні камені: Рахувати «збій» по-різному в різних командах і потім порівнювати їх між собою. Штучно дробити деплої, щоб підняти частоту. І цитувати «еталонні» пороги елітних команд з пам'яті: вони змінюються від звіту до звіту, тож краще порівнювати команду з нею самою в динаміці.

Уточнююче питання: «Що QA може зробити для lead time?» — паралелізувати і стабілізувати тести, перенести повільний e2e з блокуючого етапу в нічний, лишивши в пайплайні швидкий критичний набір.

What it is: DORA metrics are four software delivery performance measures from the DevOps Research and Assessment research: deployment frequency, lead time for changes (from commit to production), change failure rate (the share of deployments causing a failure) and time to restore service, called failed deployment recovery time in more recent reports. The first two describe speed, the last two stability.

Where QA fits: None of them is called "testing", yet QA affects all four. A slow or flaky test pipeline stretches lead time and lowers deployment frequency. Gaps in regression raise the change failure rate. Fast post-deploy smoke checks and good alerts shorten recovery time.

From a project: In one quarter a team made 120 deployments, and 9 needed a hotfix or rollback: CFR = 9 / 120 = 7.5%. The review showed 6 of the 9 failures involved database migrations never run against production-like data. We added a pipeline step that runs migrations on an anonymised snapshot, and the next quarter had 4 failures in 140 deployments, about 2.9%.

Pitfalls: Defining "failure" differently across teams and then comparing them. Splitting deployments artificially to inflate frequency. Quoting "elite" thresholds from memory: they shift between reports, so compare a team with itself over time instead.

Follow-up question: "What can QA do for lead time?" — parallelise and stabilise tests, and move the slow e2e suite from a blocking stage to a nightly run while keeping a fast critical set in the pipeline.

Розрахунок CFR і часу відновлення за квартал
Quarterly CFR and recovery time calculation
Q3: деплоїв у прод                         = 120
    деплоїв, що потребували хотфіксу/відкату = 9
    Change failure rate = 9 / 120 = 7,5%

    Час відновлення по 9 збоях (хв): 12, 18, 25, 31, 40, 44, 58, 95, 210
    Медіана = 40 хв  (середнє 59 хв спотворене одним збоєм на 210 хв)

Причини збоїв: міграції БД - 6, конфіг - 2, код - 1
Дія QA: прогін міграцій на анонімізованому знімку проду в пайплайні

Q4: 4 / 140 = 2,9%,  медіана відновлення = 22 хв
Q3: production deployments                   = 120
    deployments needing a hotfix/rollback    = 9
    Change failure rate = 9 / 120 = 7.5%

    Recovery time for the 9 failures (min): 12, 18, 25, 31, 40, 44, 58, 95, 210
    Median = 40 min  (the 59 min mean is skewed by one 210 min failure)

Failure causes: DB migrations - 6, config - 2, code - 1
QA action: run migrations on an anonymised production snapshot in the pipeline

Q4: 4 / 140 = 2.9%,  median recovery = 22 min
🔧 Що в артефакті: Розрахунок показує CFR як просте відношення і пояснює, чому для часу відновлення краще брати медіану: один довгий інцидент тягне середнє вгору. Розбивка причин пов'язує метрику з конкретною дією QA.
🔧 What the artefact shows: The calculation shows CFR as a simple ratio and explains why the median suits recovery time better: one long incident drags the mean up. The cause breakdown links the metric to a specific QA action.
🗣 Як читати вголос DORA я пояснюю як дві пари: швидкість — частота деплоїв і lead time, стабільність — частка невдалих деплоїв і час відновлення. QA там не названо, але ми впливаємо на все: повільні нестабільні тести гальмують доставку, а дірки в регресі піднімають частку збоїв. Мені подобається рахувати change failure rate разом з розбором причин, бо тоді видно, яку перевірку додати. І я не люблю порівнювати команди між собою — краще дивитись на динаміку однієї команди.
🗣 How to say it out loud I explain DORA as two pairs: speed, meaning deployment frequency and lead time, and stability, meaning change failure rate and recovery time. QA isn't named, but we influence all of it: slow flaky tests hold delivery back, and gaps in regression push the failure rate up. I like calculating change failure rate together with a breakdown of causes, because then you can see which check to add. And I avoid comparing teams with each other; the trend within one team is more honest.
45 Що таке закон Ґудгарта і як «накручують» QA-метрики? What is Goodhart's law, and how do QA metrics get gamed?
Реліз, комунікація і стратегія метрикRelease decisions, communication and metrics strategy Senior ▶

Що це: Закон Ґудгарта в популярному формулюванні звучить так: коли показник стає ціллю, він перестає бути хорошим показником. Люди оптимізують те, за що їх оцінюють, і часто найдешевшим способом — змінюючи число, а не реальність за ним.

Як це виглядає в QA: Якщо тестувальників оцінюють за кількістю знайдених багів, один дефект дробиться на п'ять тікетів. Якщо ціль — pass rate, падаючі тести позначають skip або видаляють. Ціль по покриттю коду породжує тести без жодного асерту. Ціль по MTTR змушує закривати баги як Cannot Reproduce, щоб годинник зупинився.

Приклад з проєкту: В одній команді KPI «кількість автотестів» за пів року зріс з 800 до 2 300, а кількість пропущених у прод дефектів не змінилась. Аудит показав, що близько 40% нових тестів перевіряли лише статус 200 різних ендпоінтів. Ми прибрали KPI з оцінки людей і замінили його парою: escaped defects за реліз плюс частка критичних сценаріїв з автотестами.

Як захищатись: До кожної метрики, яка може бути ціллю, ставити контрметрику, що росте при накрутці. Використовувати метрики для діагностики процесу, а не для оцінки конкретних людей. Регулярно дивитись на сирі дані вибірково, а не лише на агрегат.

Уточнююче питання: «Отже, метрики взагалі не потрібні?» — потрібні, але як сигнал для розмови, а не як автоматичний вирок чи бонус.

What it is: Goodhart's law, in its popular form, says that when a measure becomes a target, it ceases to be a good measure. People optimise what they are judged on, often in the cheapest way — changing the number rather than the reality behind it.

What it looks like in QA: Judge testers by bugs found, and one defect gets split into five tickets. Make pass rate a target, and failing tests get marked skip or deleted. A code coverage target breeds tests without a single assertion. An MTTR target pushes people to close bugs as Cannot Reproduce so the clock stops.

From a project: In one team a KPI on the number of automated tests went from 800 to 2,300 in six months, while escaped defects did not change at all. An audit found about 40% of the new tests only checked for a 200 status on various endpoints. We removed the KPI from individual performance reviews and replaced it with a pair: escaped defects per release plus the share of critical scenarios with automated tests.

How to defend against it: Pair every metric that could become a target with a counter-metric that rises when it is gamed. Use metrics to diagnose the process, not to rate individuals. Regularly sample the raw data instead of looking only at the aggregate.

Follow-up question: "So we shouldn't use metrics at all?" — we should, but as a prompt for a conversation, not as an automatic verdict or bonus.

Метрика, спосіб накрутки і контрметрика
Metric, how it gets gamed, counter-metric
Метрика-ціль              | Як накручують                         | Контрметрика
--------------------------|---------------------------------------|-------------------------------------
Баги на тестувальника     | Один дефект дробиться на кілька       | Частка Duplicate / злитих тікетів
Pass rate                 | skip, видалення падаючих тестів       | Кількість skip і видалених тестів за тиждень
Покриття коду, %          | Тести без асертів                     | Mutation score (напр. PIT, Stryker)
MTTR дефектів             | Закриття як Cannot Reproduce          | Reopen rate, частка CR-резолюцій
Кількість автотестів      | Дрібні тести на статус-код            | Критичні сценарії з автотестами, %
Кількість тест-кейсів     | Копіпаст кейсів з іншими даними       | Escaped defects за реліз
Target metric             | How it gets gamed                     | Counter-metric
--------------------------|---------------------------------------|-------------------------------------
Bugs per tester           | One defect split into several         | Share of Duplicate / merged tickets
Pass rate                 | skip, deleting failing tests          | Skipped and deleted tests per week
Code coverage, %          | Tests without assertions              | Mutation score (e.g. PIT, Stryker)
Defect MTTR               | Closing as Cannot Reproduce           | Reopen rate, share of CR resolutions
Number of autotests       | Tiny status-code-only tests           | Critical scenarios automated, %
Number of test cases      | Copy-pasted cases with other data     | Escaped defects per release
🔧 Що в артефакті: Кожен рядок пов'язує метрику з найдешевшим способом її накрутити і з показником, який при цьому неминуче погіршиться. Mutation testing (PIT для Java, Stryker для JS) перевіряє, чи тести справді ловлять змінений код, тому викриває тести без асертів.
🔧 What the artefact shows: Each row links a metric to the cheapest way to game it and to a measure that inevitably gets worse when that happens. Mutation testing (PIT for Java, Stryker for JS) checks whether tests actually catch modified code, so it exposes assertion-free tests.
💡 Простими словами: Якщо за число платять, число виросте. Питання лише, чи виросте якість.
💡 In plain words: If you pay for a number, the number will go up. The only question is whether quality does.
🗣 Як читати вголос Закон Ґудгарта я бачив наживо: як тільки кількість автотестів стала KPI, їх стало втричі більше, а багів у проді менше не стало. Тому я ніколи не ставлю одну метрику ціллю для людей. До кожної я підбираю пару, яка росте, якщо першу накручують: до покриття — mutation score, до MTTR — reopen rate. І я періодично відкриваю сирі дані, бо агрегат завжди виглядає пристойніше, ніж реальність.
🗣 How to say it out loud I've seen Goodhart's law live: as soon as the number of automated tests became a KPI, it tripled, and production bugs didn't go down at all. So I never make a single metric a target for people. For each one I pick a partner that rises if the first is gamed: mutation score next to coverage, reopen rate next to MTTR. And every so often I open the raw data, because an aggregate always looks tidier than reality.
46 Як обрати метрики для нової команди? How do you choose metrics for a new team?
Реліз, комунікація і стратегія метрикRelease decisions, communication and metrics strategy Senior ▶

З чого почати: Не з переліку «популярних QA-метрик», а з цілей команди. Зручна рамка — Goal-Question-Metric (GQM): спершу ціль («менше інцидентів після релізу»), потім питання, яке покаже, чи ми до неї рухаємось («скільки дефектів доходить до користувачів?»), і лише потім метрика, що відповідає на питання.

Правила відбору: П'ять-сім метрик, не більше, інакше ніхто не дивиться жодну. У кожної є власник, джерело даних, частота перегляду і рішення, яке вона запускає: «якщо flaky rate понад 3% два тижні поспіль — спринт стабілізації». Метрика, яка не змінює жодного рішення, — це шум, її прибирають.

Приклад з проєкту: Я прийшов у команду з 11 показниками на дашборді, на ретро ніхто їх не обговорював. Разом з лідом ми сформулювали три цілі: стабільні релізи, швидкий зворотний зв'язок у PR і менше повторних дефектів. Залишили п'ять метрик, решту шість заархівували. Через квартал перегляд дашборду став 10-хвилинним пунктом ретро, а за двома метриками вже були ухвалені конкретні рішення.

Підводні камені: Копіювати набір метрик з іншої компанії з іншими проблемами. Збирати дані вручну — такі метрики вмирають за місяць. Починати з цілей по числах без базової лінії: спершу 4–6 тижнів просто вимірюємо.

Уточнююче питання: «Коли переглядати набір метрик?» — раз на квартал або коли змінюється ціль команди; метрика, яка три місяці не викликала жодної дії, кандидат на видалення.

Where to start: Not with a list of "popular QA metrics" but with the team's goals. A handy framework is Goal-Question-Metric (GQM): first the goal ("fewer incidents after releases"), then the question that shows whether we are moving towards it ("how many defects reach users?"), and only then the metric that answers the question.

Selection rules: Five to seven metrics at most, otherwise nobody looks at any of them. Each has an owner, a data source, a review cadence and a decision it triggers: "if flaky rate is above 3% two weeks in a row, we run a stabilisation sprint". A metric that changes no decision is noise and gets removed.

From a project: I joined a team with 11 indicators on its dashboard that nobody discussed at retros. With the lead we defined three goals: stable releases, fast feedback in PRs and fewer repeat defects. We kept five metrics and archived the other six. A quarter later the dashboard review was a 10-minute retro item, and two of the metrics had already driven concrete decisions.

Pitfalls: Copying another company's metric set built for different problems. Collecting data by hand — such metrics die within a month. Setting numeric targets without a baseline: measure for 4–6 weeks first.

Follow-up question: "When do you revisit the metric set?" — once a quarter or when the team's goal changes; a metric that triggered no action for three months is a candidate for removal.

GQM-таблиця для команди
GQM table for a team
Ціль                    | Питання                              | Метрика                          | Власник  | Поріг -> рішення
------------------------|--------------------------------------|----------------------------------|----------|-------------------------------------
Стабільні релізи        | Скільки дефектів доходить до юзерів? | Escaped defects / реліз          | QA lead  | > 3 два релізи поспіль -> розбір причин
Стабільні релізи        | Як часто реліз вимагає хотфіксу?     | Change failure rate, %           | Dev lead | > 10% за місяць -> ревізія регресу
Швидкий фідбек у PR     | Скільки чекає розробник?             | p90 часу пайплайну PR, хв        | DevOps   | > 15 хв -> шардинг / перенос e2e
Довіра до автотестів    | Чи можна вірити червоному статусу?   | Flaky rate, %                    | QA       | > 3% 2 тижні -> спринт стабілізації
Менше повторних дефектів| Чи фікси справді виправляють?        | Reopen rate, %                   | Dev lead | > 8% -> перевірка DoD для фіксів

Перегляд: щотижня на ретро, 10 хв. Ревізія набору: щокварталу.
Goal                    | Question                             | Metric                           | Owner    | Threshold -> decision
------------------------|--------------------------------------|----------------------------------|----------|-------------------------------------
Stable releases         | How many defects reach users?        | Escaped defects / release        | QA lead  | > 3 two releases running -> cause review
Stable releases         | How often does a release need a fix? | Change failure rate, %           | Dev lead | > 10% a month -> regression review
Fast PR feedback        | How long does a developer wait?      | p90 PR pipeline time, min        | DevOps   | > 15 min -> sharding / move e2e out
Trust in autotests      | Can we believe a red status?         | Flaky rate, %                    | QA       | > 3% for 2 weeks -> stabilisation sprint
Fewer repeat defects    | Do fixes really fix things?          | Reopen rate, %                   | Dev lead | > 8% -> review DoD for fixes

Review: weekly at the retro, 10 min. Revisit the set: quarterly.
🔧 Що в артефакті: Таблиця читається зліва направо: ціль, питання, метрика, хто за неї відповідає і яке рішення вона запускає. Остання колонка — головна: вона доводить, що метрику збирають заради дії, а не для звіту.
🔧 What the artefact shows: The table reads left to right: goal, question, metric, who owns it and what decision it triggers. The last column matters most: it proves the metric exists to drive an action, not just to fill a report.
🗣 Як читати вголос Коли я приходжу в нову команду, я не починаю з дашборду. Спершу питаю, що зараз болить: інциденти після релізу, повільні пайплайни, баги, що повертаються. Під кожну ціль формулюю питання і тільки тоді підбираю метрику. Я тримаю п'ять-сім метрик, і в кожної є власник і поріг, після якого ми щось робимо. Якщо метрика три місяці нічого не змінила, я її прибираю без жалю.
🗣 How to say it out loud When I join a new team I don't start with a dashboard. First I ask what hurts right now: incidents after releases, slow pipelines, bugs that keep coming back. For each goal I phrase a question, and only then pick a metric. I keep five to seven metrics, and each one has an owner and a threshold after which we actually do something. If a metric hasn't changed anything for three months, I drop it without regret.
47 Менеджмент хоче 100% pass rate — що ви відповісте? Management wants a 100% pass rate — how do you respond?
Реліз, комунікація і стратегія метрикRelease decisions, communication and metrics strategy Senior ▶

Що стоїть за запитом: Менеджер насправді хоче впевненості, що реліз безпечний. 100% pass rate здається простим способом це виміряти, але як ціль він працює проти якості: найдешевший шлях до зеленого — вимкнути або «пом'якшити» тести, що падають, а тести, що ніколи не падають, нічого не повідомляють.

Як я відповідаю: Я не відмовляю, а пропоную точніший договір. Критичний шлях — вхід, оплата, оформлення замовлення — має бути 100% зеленим, і це справді блокер релізу. Для решти набору ціль — не менше 97% з правилом: кожне падіння або є відомим відкритим багом, або в карантині з власником і терміном. І головне — додати метрику результату: скільки дефектів дійшло до користувачів.

Приклад з проєкту: Після такої вимоги в одній команді за місяць кількість test.skip зросла з 12 до 57, а pass rate став 100%. Наступний реліз приніс два інциденти саме в зонах, де тести були вимкнені. Після переходу на схему «критичний шлях 100% + карантин з SLA 5 днів» skip повернувся до 15, а escaped defects впали з 6 до 2 за реліз.

Компроміс: Інколи менеджменту потрібне одне число для звіту вище. Тоді даю його, але з поясненням, з чого воно складається: «критичний шлях 100%, загалом 98,4%, 2 тести в карантині».

Уточнююче питання: «А якщо менеджер наполягає?» — показати дані про skip і escaped defects і домовитись про пробний період нової схеми на два релізи.

What is behind the request: The manager actually wants confidence that a release is safe. A 100% pass rate looks like a simple way to measure that, but as a target it works against quality: the cheapest route to green is disabling or "softening" failing tests, and tests that never fail tell you nothing.

How I respond: I don't say no; I offer a more precise deal. The critical path — login, payment, checkout — must be 100% green, and that genuinely blocks a release. For the rest of the suite the target is at least 97% with a rule: every failure is either a known open bug or quarantined with an owner and a deadline. Most importantly, we add an outcome metric: how many defects reached users.

From a project: After such a demand, one team's number of test.skip calls grew from 12 to 57 in a month, and pass rate hit 100%. The next release brought two incidents exactly in the areas where tests had been disabled. After switching to "critical path 100% + quarantine with a 5-day SLA", skips went back to 15 and escaped defects fell from 6 to 2 per release.

Trade-off: Sometimes management needs one number for the report upstairs. Then I give it, but with its breakdown: "critical path 100%, overall 98.4%, 2 tests in quarantine".

Follow-up question: "What if the manager insists?" — show the data on skips and escaped defects and agree to trial the new scheme for two releases.

Відповідь менеджеру
Reply to the manager
Привіт, Андрію! Розумію, що потрібна впевненість у релізах. Пропоную
замість «100% усіх тестів» такий договір:

1. Критичний шлях (вхід, кошик, оплата; 64 тести) - 100% зелений.
   Будь-яке падіння блокує реліз.
2. Решта набору (1 180 тестів) - не менше 97%. Кожне падіння або
   прив'язане до відкритого бага, або в карантині з власником і
   терміном до 5 робочих днів.
3. Щоб бачити результат, а не лише процес, у звіті з'являється
   escaped defects за реліз (зараз 6, ціль - до 2 за квартал).

Чому не 100% загалом: за останній місяць skip у тестах виріс з 12
до 57, і два інциденти релізу 8.3 сталися саме у вимкнених зонах.
Готовий обговорити на 15 хв; пропоную спробувати на двох релізах.
Hi Andrii, I understand we need confidence in our releases. Instead of
"100% of all tests" I'd suggest this agreement:

1. Critical path (login, cart, payment; 64 tests) - 100% green.
   Any failure blocks the release.
2. The rest of the suite (1,180 tests) - at least 97%. Every failure is
   either linked to an open bug or quarantined with an owner and a
   deadline of up to 5 working days.
3. To see the outcome, not just the process, the report adds
   escaped defects per release (6 now, target 2 or fewer within a quarter).

Why not 100% overall: last month skips in our tests grew from 12
to 57, and both incidents in release 8.3 happened in disabled areas.
Happy to discuss for 15 minutes; let's try it for two releases.
🔧 Що в артефакті: Відповідь визнає справжню потребу менеджера, пропонує три перевірювані правила замість одного числа і підкріплює позицію даними про skip та інциденти. Завершення — конкретна пропозиція спробувати, а не відмова.
🔧 What the artefact shows: The reply acknowledges the manager's real need, offers three checkable rules instead of one number and backs the position with data on skips and incidents. It ends with a concrete trial proposal rather than a refusal.
🗣 Як читати вголос Я чую за цим запитом бажання спокійно спати в день релізу, і це нормальне бажання. Але якщо зробити 100% ціллю, люди просто почнуть вимикати тести, я таке бачив. Тому я пропоную угоду: критичний шлях завжди зелений і блокує реліз, для решти є поріг і карантин з дедлайном, а поруч ми дивимось, скільки багів дійшло до користувачів. Я приношу цифри і пропоную спробувати на двох релізах.
🗣 How to say it out loud Behind this request I hear a wish to sleep well on release day, and that's a fair wish. But if 100% becomes the target, people simply start disabling tests; I've seen it happen. So I offer a deal: the critical path is always green and blocks the release, the rest has a threshold and a quarantine with a deadline, and next to it we watch how many bugs reached users. I bring the numbers and suggest trying it for two releases.
48 Чи випустили б ви реліз з відомим критичним багом і як сформулювати ризик? Would you release with a known critical bug, and how would you phrase the risk?
Реліз, комунікація і стратегія метрикRelease decisions, communication and metrics strategy Senior ▶

Якщо коротко: Інколи так, але це рішення бізнесу, а завдання QA — описати ризик так, щоб його можна було свідомо прийняти або відхилити. «Критичний» за severity не завжди означає «критичний для цього релізу»: важить, скільки користувачів зачеплено, наскільки ймовірний сценарій і чи є спосіб обмежити шкоду.

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

Приклад з проєкту: За день до релізу знайшли Critical: експорт звіту в PDF падає для компаній з понад 500 працівниками. Таких клієнтів 11 з 2 400, звіт формують раз на місяць, найближча дата — через 9 днів. Ми випустили реліз з вимкненою кнопкою PDF для цих 11 акаунтів через флаг, повідомили їх через account-менеджерів, фікс вийшов на 5-й день.

Коли відповідь — ні: Втрата або пошкодження даних без можливості відновлення, вразливість з доступом до чужих даних, некоректні суми в платежах чи нарахуваннях, порушення регуляторних вимог. Тут жодні умови не компенсують ризик, і я пишу NO-GO прямо.

Уточнююче питання: «А якщо ризик прийняли, а він стався?» — діємо за планом відкату, а на ретро розбираємо не «хто винен», а чи правильно ми оцінили ймовірність.

In short: Sometimes, yes, but it is a business decision; QA's job is to describe the risk well enough that it can be consciously accepted or rejected. "Critical" by severity is not always "critical for this release": what matters is how many users are affected, how likely the scenario is and whether the damage can be contained.

How to phrase it: A risk statement has a fixed shape: what exactly is broken, the share of users, likelihood, impact, mitigation (feature flag, workaround, alert), rollback plan, owner, and who decides by when. Without mitigation it is no longer an accepted risk, it is a hope.

From a project: A day before release we found a Critical: PDF report export crashed for companies with more than 500 employees. That was 11 of 2,400 customers, the report runs monthly, and the next run was 9 days away. We shipped with the PDF button switched off by a flag for those 11 accounts, told them through their account managers, and the fix went out on day 5.

When the answer is no: Loss or corruption of data that cannot be restored, a vulnerability exposing other customers' data, wrong amounts in payments or billing, breaking regulatory requirements. No conditions offset those risks, and I write NO-GO plainly.

Follow-up question: "What if the accepted risk materialises?" — follow the rollback plan, and at the retro look not for someone to blame but at whether we estimated the likelihood correctly.

Шаблон risk statement
Risk statement template
RISK STATEMENT - реліз 6.4.0, дефект REP-902 (Critical)
Що зламано:     експорт звіту в PDF падає з 500 для компаній > 500 працівників
Кого зачіпає:   11 з 2 400 клієнтів (0,46%), список у тікеті
Імовірність:    висока для цих 11, звіт формують 1-го числа (через 9 днів)
Наслідки:       клієнт не отримує місячний звіт; даних не втрачено
Пом'якшення:    флаг pdf_export вимкнено для 11 акаунтів; XLSX працює;
                account-менеджери повідомляють клієнтів до 12:00 завтра
Моніторинг:     алерт на 5xx /reports/export > 1% за 10 хв
Відкат:         не потрібен (флаг); фікс REP-902 у 6.4.1, ETA 5 днів
Власник:        Dev lead команди Reports
Рішення:        Product owner, до 16:00 сьогодні
Рекомендація QA: GO з умовами (флаг + повідомлення клієнтам)
Стоп-фактор:     NO-GO, якщо з'ясується, що збій пошкоджує збережені звіти
RISK STATEMENT - release 6.4.0, defect REP-902 (Critical)
What is broken: PDF report export returns 500 for companies > 500 employees
Who is hit:     11 of 2,400 customers (0.46%), list in the ticket
Likelihood:     high for those 11; the report runs on the 1st (9 days away)
Impact:         customer does not get the monthly report; no data lost
Mitigation:     flag pdf_export off for the 11 accounts; XLSX works;
                account managers notify customers by 12:00 tomorrow
Monitoring:     alert on 5xx /reports/export > 1% over 10 min
Rollback:       not needed (flag); REP-902 fix in 6.4.1, ETA 5 days
Owner:          Reports team dev lead
Decision:       Product owner, by 16:00 today
QA recommendation: GO with conditions (flag + customer notice)
Stop factor:    NO-GO if the failure turns out to corrupt stored reports
🔧 Що в артефакті: Кожне поле відповідає на окреме питання того, хто ухвалює рішення: масштаб, імовірність, наслідки, як обмежено шкоду і як ми дізнаємось про проблему. Рядок «Стоп-фактор» заздалегідь називає нову інформацію, яка перетворить GO на NO-GO.
🔧 What the artefact shows: Each field answers a separate question the decision-maker has: scale, likelihood, impact, how damage is contained and how we will find out about trouble. The stop-factor line names in advance the new information that would turn GO into NO-GO.
🗣 Як читати вголос Я не кажу «ніколи» — я кажу, що це рішення бізнесу, а моя робота — зробити ризик зрозумілим. Я пишу risk statement: скільки людей зачепить, наскільки ймовірно, що буде найгіршого, як ми обмежимо шкоду і хто вирішує. Якщо можна вимкнути функцію флагом для кількох клієнтів і попередити їх, випуск може бути розумним. Але втрата даних, чужі дані чи неправильні гроші — це для мене завжди NO-GO, без торгу.
🗣 How to say it out loud I don't say "never". I say it's a business decision, and my job is to make the risk understandable. I write a risk statement: how many people it hits, how likely it is, what the worst outcome looks like, how we contain it and who decides. If we can switch the feature off by flag for a few customers and warn them, shipping can be reasonable. But data loss, someone else's data exposed or wrong money is always a NO-GO for me, no bargaining.
49 Метрики зелені, а користувачі скаржаться — що ви робите? The metrics are green, but users complain — what do you do?
Реліз, комунікація і стратегія метрикRelease decisions, communication and metrics strategy Senior ▶

Що це означає: Розрив між зеленим дашбордом і скаргами — майже завжди ознака того, що ми вимірюємо не те, що переживає користувач. Тести перевіряють сценарії, які ми собі уявили, на даних і пристроях, які ми обрали. Скарги приходять з реальних браузерів, мереж, обсягів даних і шляхів, яких ніхто не передбачив.

Як я розслідую: Спершу категоризую скарги: тікети підтримки, відгуки в App Store і Google Play, повідомлення в соцмережах. Потім зводжу їх з продовими сигналами — рівнем помилок, RUM-метриками швидкодії, логами. Далі порівнюю топ-категорії скарг з тим, що покрито тестами: де розрив, там і сліпа зона.

Приклад з проєкту: Регрес 100% зелений, але за два тижні 140 звернень про «повільний пошук». Тести ганяли пошук на базі з 5 тисяч товарів, а у 30 найбільших клієнтів їх понад 400 тисяч. RUM показав p75 часу відповіді пошуку 6,8 с саме для цих акаунтів. Ми додали перфоманс-тест на великому датасеті і метрику p75 пошуку з проду на дашборд якості.

Підводні камені: Списувати скарги на «користувачі не вміють» або на поодинокі випадки без даних. Шукати винного в QA замість того, щоб закрити сліпу зону. І не змінити дашборд після розслідування — тоді ситуація повториться.

Уточнююче питання: «Які сигнали з проду додати на дашборд якості?» — escaped defects, інциденти від клієнтів, рівень помилок і p75 ключових дій за RUM.

What it means: A gap between a green dashboard and complaints almost always means we measure something other than what users experience. Tests check the scenarios we imagined, on the data and devices we chose. Complaints come from real browsers, networks, data volumes and paths nobody anticipated.

How I investigate: First I categorise the complaints: support tickets, App Store and Google Play reviews, social media mentions. Then I line them up with production signals — error rates, RUM performance metrics, logs. Next I compare the top complaint categories with what our tests cover: where there is a gap, there is a blind spot.

From a project: Regression was 100% green, yet two weeks brought 140 tickets about "slow search". Our tests ran search against a catalogue of 5,000 products, while the 30 largest customers had more than 400,000. RUM showed a p75 search response of 6.8 s exactly for those accounts. We added a performance test on a large dataset and put production p75 for search on the quality dashboard.

Pitfalls: Writing complaints off as "users don't know how to use it" or as rare cases without data. Looking for someone in QA to blame instead of closing the blind spot. And leaving the dashboard unchanged after the investigation, which guarantees a repeat.

Follow-up question: "Which production signals belong on a quality dashboard?" — escaped defects, customer-reported incidents, error rate and p75 of key actions from RUM.

Запит: скарги за функціями проти покриття тестами
Query: complaints by feature versus test coverage
-- Скарги підтримки за останні 14 днів у розрізі функцій,
-- поруч - скільки автотестів покриває кожну функцію
SELECT f.name                           AS feature,
       COUNT(DISTINCT t.id)             AS tickets_14d,
       COALESCE(c.test_count, 0)        AS automated_tests,
       MAX(r.p75_ms)                    AS prod_p75_ms
FROM support_tickets t
JOIN features f            ON f.id = t.feature_id
LEFT JOIN (SELECT feature_id, COUNT(*) AS test_count
           FROM test_cases WHERE automated = true
           GROUP BY feature_id) c      ON c.feature_id = f.id
LEFT JOIN rum_daily r      ON r.feature_id = f.id
                          AND r.day >= CURRENT_DATE - 14
WHERE t.created_at >= CURRENT_DATE - 14
GROUP BY f.name, c.test_count
ORDER BY tickets_14d DESC
LIMIT 10;
-- Результат: search | 140 | 22 | 6800  -> тести є, але на малих даних
-- Support tickets over the last 14 days by feature,
-- next to how many automated tests cover each feature
SELECT f.name                           AS feature,
       COUNT(DISTINCT t.id)             AS tickets_14d,
       COALESCE(c.test_count, 0)        AS automated_tests,
       MAX(r.p75_ms)                    AS prod_p75_ms
FROM support_tickets t
JOIN features f            ON f.id = t.feature_id
LEFT JOIN (SELECT feature_id, COUNT(*) AS test_count
           FROM test_cases WHERE automated = true
           GROUP BY feature_id) c      ON c.feature_id = f.id
LEFT JOIN rum_daily r      ON r.feature_id = f.id
                          AND r.day >= CURRENT_DATE - 14
WHERE t.created_at >= CURRENT_DATE - 14
GROUP BY f.name, c.test_count
ORDER BY tickets_14d DESC
LIMIT 10;
-- Result: search | 140 | 22 | 6800  -> tests exist, but on small data
🔧 Що в артефакті: Запит зводить три джерела — тікети підтримки, каталог тестів і RUM — в одну таблицю за функціями. Функція з багатьма скаргами і високим p75 при наявних тестах вказує, що тести перевіряють не ті умови; без тестів — що сценарій узагалі не покрито.
🔧 What the artefact shows: The query joins three sources — support tickets, the test catalogue and RUM — into one table by feature. A feature with many complaints and a high p75 despite existing tests means the tests check the wrong conditions; with no tests, the scenario is not covered at all.
🗣 Як читати вголос Якщо дашборд зелений, а користувачі скаржаться, я вірю користувачам. Це означає, що ми міряємо свої уявлення, а не їхній досвід. Я збираю скарги з підтримки й відгуків у категорії, накладаю на продові дані про помилки та швидкість і дивлюсь, де скарги є, а тестів немає або вони на іграшкових даних. А потім обов'язково додаю продовий сигнал на дашборд, щоб наступного разу побачити проблему раніше за клієнтів.
🗣 How to say it out loud If the dashboard is green and users are complaining, I believe the users. It means we're measuring our assumptions, not their experience. I group complaints from support and reviews into categories, overlay production data on errors and speed, and look for places with complaints but no tests, or tests on toy data. Then I always add a production signal to the dashboard, so next time we see the problem before the customers do.