How to Write a Good Bug Report (With a Checklist)

QA

A good bug report gets fixed. A bad one gets a "cannot reproduce" label, a request for more information the reporter never answers, and a slow death at the bottom of the backlog. The difference is rarely effort — it's knowing what the person fixing the bug actually needs.

This guide covers the structure of a report that survives triage, whether you're filing it yourself or teaching users and teammates to file better ones.

The anatomy of a useful report

A title that describes the failure, not the feeling

"Checkout is broken" tells a developer nothing. "Checkout: clicking Pay shows a spinner forever after applying a discount code" tells them where to look, what to try, and hints at the trigger. A good title states the location, the action, and the observed result.

Steps to reproduce

Write the shortest sequence that triggers the bug, starting from a known state:

  1. Log in as a customer with an item in the cart

  2. Apply discount code SAVE10

  3. Click Pay

Numbered steps matter because the fixer will follow them literally. If step 2 is essential and you omit it, the bug reproduces for you and not for them — and now you're both stuck.

Expected vs. actual behavior

Two short lines: what should have happened, what happened instead. This sounds bureaucratic until you hit a bug where they disagree about what "correct" even is. "Expected: order confirmation page. Actual: infinite spinner, no network activity after the click" removes the ambiguity.

The environment

Browser and version, operating system, device, screen size, and the exact page URL. An enormous number of front-end bugs are environment-specific — a Safari-only date parsing issue, a layout that only breaks at tablet widths. Without environment data, the developer tests in their own browser, sees nothing, and closes the report.

The evidence

This is what separates a report that takes five minutes to diagnose from one that takes a day:

  • Console output — the actual error message and stack trace, not a paraphrase of it.

  • Network activity — which request failed, with what status code.

  • A screenshot or recording — especially for visual and layout bugs, where a thousand words genuinely are worse than one picture.

  • Timing — when it happened, so it can be matched against server logs and deploys.

Why users won't do any of this

Here's the uncomfortable part: this checklist is reasonable to demand from a QA engineer and absurd to demand from a user. Users don't know what a console is, won't look up their browser version, and will not write reproduction steps. Their honest best effort is "it doesn't work" — and every field you add to a bug form loses more of the reports.

You have two options. You can accept vague reports and pay the cost at triage time. Or you can collect the technical half of the report automatically and let the user contribute the one thing only they have: what they were trying to do.

This is the model bug reporting widgets are built on. With a tool like BugDump, a user clicks a button and types one sentence; the widget attaches the console logs, network requests, session replay, environment details, and page URL on its own. The user writes "clicked pay, nothing happened" — and that's fine, because the uncaught exception and the failed request are right there in the report.

The checklist

For reports a human writes end to end (internal QA, teammate reports, GitHub issues):

  • Title states location + action + observed result

  • Numbered steps to reproduce, from a known starting state

  • Expected behavior, in one line

  • Actual behavior, in one line

  • Environment: browser + version, OS, device, viewport, URL

  • Console errors, copied verbatim

  • Failing network request and status code, if any

  • Screenshot or recording for anything visual

  • Severity and impact: who hits this and how often?

For reports collected from users, the checklist changes shape — most boxes should be ticked by tooling, not people:

  • Description in the user's own words (the only mandatory field)

  • Everything else — environment, console, network, replay — captured automatically

  • Reporter identity attached automatically for logged-in users, so follow-up is possible

One report, one bug

Resist the urge to bundle. "The dashboard is slow, also the export button is broken, and dark mode looks wrong" is three bugs wearing one report's ID. Bundled reports can't be prioritized (the severities differ), can't be closed (fixing one leaves the report two-thirds open), and can't be assigned cleanly. File three reports. If your tracker organizes reports into tasks — BugDump turns reports into cards on a kanban board — the discipline pays off immediately, because each card can move independently.

Severity honesty

Mark severity by impact, not by frustration. A typo you personally find embarrassing is low severity; a payment failure affecting one browser is high, even if you can't reproduce it. Reports that are always marked "critical" train the team to ignore the field entirely — which is worse than not having it.

The payoff

Good bug reports compound. Each one that includes real evidence gets fixed faster, which keeps the backlog short, which keeps triage fast, which leaves more time for the reports that are genuinely hard. And the habit is contagious: developers who receive great reports start writing them.

Write the report you'd want to receive at 5 p.m. on a Friday — and automate everything the reporter shouldn't have to know how to provide.