Skip to content
Independent guides for QA & test automationRSSEditorial policy
QA Vibes

How to Write High-Quality Bug Reports

We found a real bug in a practice shop and wrote it up two ways. The difference between 30 and 100 points is steps, expected versus actual, environment, reproducibility, and evidence. Each part is explained with the exact lines from the report.

QA Vibes EditorialPublished Updated 8 minTested with Playwright 1.63.0, Chromium 153Revision history ↓

Key takeaways

  • A one-line report scored 20/100 in our grader; a complete report of the same bug scored 100.
  • Steps should start from a known state and use exact labels and values.
  • Try one variation before filing: typing and pasting the same value behaved differently.
  • Quote error messages exactly and attach a screenshot or trace.
  • Put urgency in the priority field, not in the text of the report.
Contents (10 sections)

Introduction

A developer reading a bug report has one question: can I make this happen on my machine? If the answer is no, the report comes back with questions, and the bug waits. A good bug report answers that question, and the next three, before anyone has to ask.

Advice on bug reports is easy to agree with and hard to apply, so this guide uses a real bug. Sauce Demo is a public practice shop, and its problem_user account is deliberately broken. We found a checkout bug there, wrote it up in one line, then wrote it up properly, and scored both versions with our free bug report grader.

Version Words Grader score
One-line report 23 20 / 100, "Will bounce back"
Complete report 171 100 / 100, "Ready to file"

The bug

An automated checkout test failed for problem_user: after filling in the form, the page showed "Error: Last Name is required", even though the test had filled in a last name.

Before reporting it, we reproduced it by hand and tried two ways of entering text:

  • Typing "Ada" in First Name, then "Lovelace" in Last Name: each character typed into Last Name replaced the First Name value. First Name ended up as "e", the last letter typed, and Last Name stayed empty.
  • Pasting the same values: First Name ended up as "Lovelace", and Last Name stayed empty.

We repeated each method three times with the same result. The same steps with standard_user worked. That's what the screen looked like after typing:

Sauce Demo checkout form after typing: First Name shows "e", Last Name is empty, and a red banner reads "Error: Last Name is required".

Those two extra minutes of testing matter. "Typed and pasted input behave differently" and "only this account is affected" are exactly the clues that send a developer to the right event handler instead of the whole form.

The one-line report: 20 points

This is how bugs often get reported in a chat message or a hurried ticket:

Checkout broken for problem user
 
Can't enter my name at checkout, it keeps giving an error. Please fix asap, this is really annoying.

The grader failed seven of its checks and gave one partial score:

  • No steps to reproduce. Which page? Which field? What was typed?
  • No expected or actual result. "Keeps giving an error" doesn't say which error, or what should have happened.
  • No environment. Which site, browser, and account?
  • No evidence. No screenshot, no exact error text.
  • No impact. It doesn't say who is affected or whether there's a workaround.
  • No severity or priority. The team can't tell whether to stop what they're doing.
  • Too short to act on. 23 words.
  • Tone, partial. "Please fix asap, this is really annoying" adds pressure, not information.

The developer's first reply would be a list of questions, and the bug would wait a day for the answers.

Anatomy of a strong report

Each part below shows the exact line from the complete report and the question it answers.

Title: what broke, where, for whom

Checkout: Last Name input writes into First Name for problem_user

A title is read in lists, search results, and notifications. Put the area first ("Checkout"), then the behavior, then the scope. Avoid "broken", "doesn't work", and "issue": they describe your frustration, not the bug.

Environment: where it happened

Environment: https://www.saucedemo.com, account problem_user, Chromium 153 (Playwright 1.63.0), Windows 11. Observed 2026-09-15.

Include the URL or build, the account or role, the browser and version, the operating system, and the date. For a mobile app, add the app version and device model. The date matters more than it looks: it lets someone match the report to a deploy.

Steps to reproduce: a path anyone can follow

  1. Open https://www.saucedemo.com and log in as problem_user.
  2. Click "Add to cart" on Sauce Labs Backpack.
  3. Open the cart and click "Checkout".
  4. Type "Ada" in First Name.
  5. Type "Lovelace" in Last Name.
  6. Type "10115" in Zip/Postal Code and click "Continue".

Three rules make steps reproducible:

  1. Start from a known state, such as logging in, not from "on the checkout page".
  2. One action per step, using the exact label on the screen in quotes.
  3. Give the exact values you entered. "Enter a name" hides the fact that the bug depends on typing a second value.

Expected and actual: the gap is the bug

Expected result: First Name shows "Ada", Last Name shows "Lovelace", and Continue opens "Checkout: Overview".

Actual result: Each character typed in Last Name replaces the First Name value, so First Name shows "e" and Last Name stays empty. Continue shows "Error: Last Name is required" and checkout cannot continue. Pasting the whole value gives First Name "Lovelace" instead.

Write both, even when the expected result feels obvious. Quote error messages exactly, character for character, so they can be searched in the code and logs.

Reproducibility: how often, and what's not affected

Reproducible: 6 of 6 attempts (3 typed, 3 pasted). standard_user is not affected.

"Always" and "sometimes" are opinions; "6 of 6" is data. Saying what is not affected narrows the search as much as saying what is.

Impact: why it matters

Impact: Any customer on this account cannot complete checkout, so every order is blocked. There is no workaround in the UI.

Impact is what turns a bug into a decision. Say who is affected, what they can't do, and whether there's a workaround.

Severity and priority: how bad, how soon

Severity: Major Priority: P2

Severity is how bad the effect is; priority is how soon it should be fixed. They're separate decisions, and the product owner may change the priority. We chose Major rather than Blocker because only one account is affected, and P2 because it's a practice shop. The same bug on every account of a real store would be a Blocker and P1.

Evidence: proof, not a description

Evidence: screenshot problem-user-last-name.png and Playwright trace trace.zip attached.

A screenshot shows the state; a trace or screen recording shows how you got there. For API bugs, attach the request and response, or a curl command that reproduces it. Crop or mask anything sensitive first: tokens, emails, and customer data don't belong in a ticket.

The complete report

This is the full report that scored 100. Copy the structure for your own:

Checkout: Last Name input writes into First Name for problem_user
 
Environment: https://www.saucedemo.com, account problem_user, Chromium 153 (Playwright 1.63.0), Windows 11. Observed 2026-09-15.
Severity: Major
Priority: P2
 
Steps to reproduce:
1. Open https://www.saucedemo.com and log in as problem_user.
2. Click "Add to cart" on Sauce Labs Backpack.
3. Open the cart and click "Checkout".
4. Type "Ada" in First Name.
5. Type "Lovelace" in Last Name.
6. Type "10115" in Zip/Postal Code and click "Continue".
 
Expected result: First Name shows "Ada", Last Name shows "Lovelace", and Continue opens "Checkout: Overview".
 
Actual result: Each character typed in Last Name replaces the First Name value, so First Name shows "e" and Last Name stays empty. Continue shows "Error: Last Name is required" and checkout cannot continue. Pasting the whole value gives First Name "Lovelace" instead.
 
Reproducible: 6 of 6 attempts (3 typed, 3 pasted). standard_user is not affected.
 
Impact: Any customer on this account cannot complete checkout, so every order is blocked. There is no workaround in the UI.
 
Evidence: screenshot problem-user-last-name.png and Playwright trace trace.zip attached.

Paste your own report into the bug report grader before filing. It runs in your browser, sets severity and priority fields for you, and exports Markdown or Jira markup.

Tone: describe the software, not the people

The one-line report ends with "Please fix asap, this is really annoying." Urgency belongs in the priority field, and frustration doesn't help anyone find the bug. Compare:

  • "Developers broke checkout again."
  • "Checkout: Last Name input writes into First Name for problem_user."

The second is shorter, searchable, and can't start an argument.

Writing this article caught two mistakes in our own grader. When we first scored the one-line report, it got 30 points: the tone check passed "asap, really annoying", and the impact check passed because the title mentions a "user". We fixed both. Pressure words now earn a partial tone score, and naming a user in the title no longer counts as describing impact. The report now scores 20, and both cases are unit tests so they can't come back. Treat any automated check, including ours, as a minimum, not a guarantee.

One bug per report

When you find several problems in one session, file them separately. On the same problem_user account, every product shows the same placeholder image, a different bug from the checkout form. One report per bug means each can be assigned, fixed, and closed on its own, and none gets lost when the other is marked done.

Checklist before you file

  • The title names the area, the behavior, and the scope.
  • The steps start from a known state and use exact labels and values.
  • Expected and actual results are both written, with error text quoted exactly.
  • You reproduced it at least twice and wrote down how often it happens.
  • You checked one variation (another account, browser, or input method) and wrote down the result.
  • Impact, severity, and priority are set.
  • A screenshot, trace, log, or request is attached, with sensitive data removed.
  • You searched for an existing report of the same bug.

Conclusion

The difference between our two reports wasn't writing talent. It was ten minutes of work before writing: reproducing the bug, trying one variation, and copying the exact error. A report built from those facts answers the developer's questions in advance, which is the fastest way to get a bug fixed.

Sources and further reading

Tools mentioned

JiraIssue TrackingFree plan
LinearIssue TrackingFree plan
TestRailTest ManagementFree trial
PlaywrightUI AutomationOpen source

Links go to each tool’s official site. How we choose and link tools

Revision history

Rewritten around a real, reproducible bug on the Sauce Demo practice shop, with both report versions scored by our bug report grader. Writing it exposed two grader mistakes (impact and tone), which we fixed; the one-line report's score dropped from 30 to 20.
Revised during a site-wide content audit.
First published.

Spotted a mistake? Report it — corrections land here.

Written and reviewed by

QA Vibes Editorial

Articles are written and reviewed by practicing QA and automation engineers. Every article lists its sources and shows when it was last updated.

Try it on a real report

Bug report grader

Paste a draft and score it against ten checks before you file it.