How to write a bug report developers can act on
A good bug report lets someone who was not there see the problem, judge how bad it is and know when it is fixed. This guide goes field by field, and ends with one billing bug written badly and then well.
By the VeoRec team · · 11 min read
In short
A bug report is good when a developer can reproduce the problem, judge its impact and confirm the fix without asking you anything. That takes a specific title, the environment, numbered steps from a clean start, expected and actual results side by side, evidence such as a recording or the exact error text, and an honest severity. One bug per report, written as observations rather than guesses.
- Name the symptom and the place in the title: "Saving the billing address clears the VAT number" beats "Checkout broken".
- Capture the environment while you still have it: browser and version, OS, account role, build, URL.
- Write expected and actual results as two separate lines, and report what you saw, not what you think caused it.
- Attach evidence that answers the next question: a short recording, the exact error text, the failing request.
- Rate severity by impact on users; leave priority to whoever plans the work.
- File one bug per report and check for duplicates first; two bugs in one ticket means one of them gets lost.
"Checkout is broken" is a feeling, not a fault. It tells a developer that someone is unhappy. It does not say which checkout, for whom, after which click, or what "broken" looked like. So the developer writes back, you answer a day later, and a ten minute fix takes a week. This guide covers how to write a bug report that skips that loop: the fields that matter, what to put in each one, and a worked example you can copy.
None of this is new. Mozilla, Chromium and most large open source projects publish guidance on filing bugs, and they agree on almost everything. What follows is that shared advice, with the reasoning behind each field and the shortcuts that work on a real product team.
Know what the developer needs before you write a word
A developer reading your report has three questions. Can I make this happen on my machine? How bad is it? How will I know it is fixed? Every field in a good report serves one of those three questions, and anything that serves none of them can go.
That gives you a short list of fields:
- Title: the symptom and where it happens, in one line.
- Environment: browser, OS, device, account, build: whatever could make your setup different from theirs.
- Steps to reproduce: numbered, from a clean starting point, one action per step.
- Expected result and actual result: what should have happened, then what did.
- Evidence: a screenshot or recording, the exact error text, console output, the failing request.
- Severity and frequency: how many users it hurts, how badly, and whether it happens every time.
Mozilla calls the steps "the most important part of any bug report" in its bug writing guidelines, and that matches experience. A report with a vague title and perfect steps still gets fixed. A report with a perfect title and no steps usually stalls.
Write a title that names the symptom and the place
The title is what people see in a backlog of two hundred tickets, in a search for duplicates and in a release note. It has to work without the body. A useful pattern is where, what happens, under which condition.
| Weak title | Strong title | What changed |
|---|---|---|
| Checkout broken | Saving the billing address clears the VAT number field | Names the action and the visible symptom |
| Login issue | Password reset link returns 404 when opened on mobile Safari | Adds the condition that makes it happen |
| Dashboard slow | Reports page takes 40 s to load for accounts with 10k+ invoices | Puts a number on "slow" and says who is affected |
| Button not working!!! | Export CSV button does nothing on the Invoices tab (no download, no error) | Says what "not working" looked like |
Keep it under about 60 characters where you can; Mozilla gives the same advice. Leave out jargon only your team uses, and leave out the fix. "Disable the Save button after the first click" is a solution, and it may be the wrong one. "Double-clicking Save creates two orders" is the problem, and it lets the developer pick the fix.
Record the environment while you still have it
A lot of "works on my machine" replies come down to a difference nobody wrote down. Note the environment at the moment you see the bug, because by tomorrow your browser will have updated and the test account will have been reset.
- Browser and version. In Chrome,
chrome://versionshows the exact build. "Chrome" alone is not enough when a bug arrived with one release. - Operating system and device. Include the phone model for mobile web; screen size matters for layout bugs.
- Where in the product. The full URL, the environment (production, staging, a preview build) and the app version or commit if your team shows one.
- Who you were. Account role, plan, feature flags, language and time zone. Many bugs only exist for admins, for the free plan, or after midnight UTC.
- What was unusual. Browser extensions, a VPN, a slow connection, a very long customer name.
You do not have to list everything every time. List what could plausibly matter, and say what you already ruled out: "Also happens in a private window, so not an extension" saves the developer a round trip.
When the bug comes from a customer, you are writing the report on their behalf, and the environment is the part that goes missing. Before you pass it on, ask them for three things: the page address, roughly what time it happened, and the browser they use. The time is the one people forget, and it is often the most valuable, because it lets a developer find the customer's actual request in the server logs instead of guessing at it. If the customer can send a short recording of their screen, even better: it shows the environment without anyone having to describe it.
Write steps to reproduce from a clean start
Steps are a recipe. Start from a state anyone can reach ("signed in as a workspace admin, on an empty cart"), then number every action, one per line, with the exact data you used. If the bug needs a particular record, name it or say how to create it.
- Sign in as an admin on staging (any workspace on the Business plan).
- Go to Settings, then Billing.
- Enter
DE123456789in VAT number and click Save. The field keeps the value. - Change Address line 2 to
Floor 3and click Save again.
Then say how often it happens: "every time", "3 of 10 attempts", "once, could not repeat". An honest "once" is useful; a guessed "always" sends someone hunting for a bug they cannot see. Steps are the field where most reports go wrong, so there is a whole guide on writing steps to reproduce, including how to shrink a long sequence and what to do with bugs that only happen sometimes.
The most common hole in steps is a precondition you did not know you had. In the VAT example, the bug might only happen because your test workspace was created before a migration and still has an old address format. Your steps work perfectly for you and fail for everyone else. The cheap check is to run your own steps once on a fresh account, or ask a colleague to run them cold. If they cannot reproduce it, the difference between your account and theirs is now the most interesting fact in the report, so write it down: "Does not happen on a workspace created today; does happen on the demo workspace from March."
Put expected and actual results on separate lines
Write what should have happened, then what did. Two lines, two labels. It sounds trivial, but it catches a whole class of non-bugs early: sometimes writing "Expected" makes you realise you are not sure what the product is meant to do, and the right next step is a question to the product owner, not a ticket.
Expected: after the second Save, the VAT number still shows DE123456789.
Actual: the VAT number field is empty. Reloading the page shows it empty too, so the value was removed on the server, not just in the form.
Notice that the actual result reports observations, including the reload check. It does not say "the API overwrites the field with null". Maybe it does. But Simon Tatham, in his long-standing essay How to Report Bugs Effectively, makes the case well: always state the symptoms, and treat your own diagnosis as an optional extra, never a replacement for them. Mozilla's guidelines say the same thing more briefly: separate facts from speculation. A wrong diagnosis in the actual result sends the developer down your wrong path first.
Attach evidence that answers the next question
Evidence is whatever lets the developer see what you saw. Pick the kind that answers the question they would ask next.
- A screenshot for anything static: a broken layout, wrong text, a misaligned price. Crop or annotate it so the problem is obvious in a thumbnail.
- A screen recording when the bug involves a sequence, timing or motion: a double submit, a flicker, a dropdown that closes on its own. More on that in video bug reports.
- Console errors. Open DevTools, reproduce, and copy the red lines. Many teams ask for these; the VS Code project lists Dev Tools console errors in its guide to submitting bugs.
- The failing network request. The status code, the endpoint and the response body are often the whole diagnosis.
If someone asks for a HAR file (a full log of network traffic), be careful with it. It can contain session cookies and tokens. Recent Chrome versions export a sanitised HAR by default, without Cookie, Set-Cookie and Authorization headers, as the Chrome DevTools notes for version 130 explain. Even so, attach it to a private ticket, not a public issue.
Not sure whether a picture is enough? Answer three questions:
A recording also works as a safety net for your written steps. If a step is ambiguous, the developer can watch you do it. VS Code's guide puts the limit well: images and animations illustrate the steps but do not replace them. With VeoRec, the clicks are highlighted in the video and the share link is already copied when you stop, so adding one to a ticket takes seconds rather than an export and an upload.
Rate severity by impact and leave priority to the team
Severity is how bad the bug is for the people who hit it. Priority is when the team will fix it. They are related, but they are different decisions, made by different people. You, as the reporter, are usually the best judge of severity and often the worst judge of priority, because you cannot see the rest of the backlog.
Mozilla publishes its severity definitions for Firefox, and they adapt well to most products. A simplified version:
| Level | Means | Example |
|---|---|---|
| S1, critical | Blocks a core task for many users, loses data, or has no workaround | Payments fail for every card; saved documents disappear |
| S2, major | A major feature is badly impaired and the workaround is poor | Export works only one page at a time |
| S3, normal | Something is wrong, but a reasonable workaround exists or few users are affected | VAT number clears on second save; re-entering it works |
| S4, minor | Cosmetic or very low impact | Tooltip text is cut off at 1280 px wide |
Explain the rating in one sentence: "S3: every customer who edits billing twice loses their VAT number, which puts the wrong tax on their next invoice." That sentence is what lets a product manager move it up or down with confidence. Frequency belongs next to it. A crash that happens once in a thousand sessions and a typo on the pricing page can both be the right thing to fix this week, for very different reasons.
How to write a bug report: a worked example
Take the VAT bug from the sections above and write it twice. The first version is what usually lands in a team chat. The second is the same information, arranged so nobody has to ask.
The second version is longer, but it is faster for everyone. The developer can reproduce it in a minute, the product manager can rank it without a meeting, and whoever tests the fix knows exactly what "fixed" means: step 3, then the field still shows the value.
Try it on a bug you have open right now. Fill in the fields and copy the result as Markdown, ready to paste into Jira, Linear or a GitHub issue:
If your team files a lot of reports, turn this structure into a template in your tracker so the fields are there before anyone starts typing. There is a set of ready-made ones for different kinds of bugs in bug report templates.
Habits that make your reports easier to fix
The fields get a report through the door. These habits are what make developers glad to see your name on a ticket.
- Stop and write before you poke at it. When something odd happens, note what you just did before you click around trying to make it go away. Tatham's essay has a good passage on freezing instead of panicking: the first few seconds hold the best clues.
- One bug per report. If you found three problems on the same page, file three tickets and link them. Mozilla and VS Code both ask for this in their guides, because a ticket with two bugs gets closed when one of them is fixed.
- Search before you file. Look for the error text and the feature name in the tracker. If a report exists, add your environment and evidence to it instead of starting a new one.
- Retest on the latest build. A bug that is already fixed on staging is not worth a developer's afternoon. Mozilla's guidelines ask reporters to try an in-development version such as Firefox Nightly for the same reason.
- Write for a stranger. Spell out what "it", "the modal" and "the new flow" refer to. The person who picks up the ticket in three weeks may not be the one you were talking to today.
- Keep blame out of it. "The VAT field clears" is a fact. "Someone broke billing again" is a mood, and it makes people defensive rather than curious.
Show the bug instead of describing it Record the tab where it happens with your voice over it. Clicks are highlighted, the link is copied when you stop, and the developer can reply at the exact second it goes wrong. See how bug reporting works
Mistakes that send a report back to you
If your reports keep coming back with questions, look for these first:
- The steps start in the middle. "Open the modal and click Next" assumes the reader knows which modal and how you got there.
- "It doesn't work." Say what happened instead: nothing at all, a spinner forever, an error, the wrong data.
- Data you cannot share. Steps that need a specific customer's account need a test equivalent, or a note on which record to use and how to get access.
- A screenshot of a screenshot. A photo of a monitor or a cropped image with no URL bar loses the context. Capture the browser itself.
- Severity inflation. If every report is critical, none of them are. Teams learn to discount reporters who cry wolf.
- A diagnosis instead of a description. "The cache is stale" may be right, but put it under a separate "Notes" heading so the facts stand on their own.
When the bug is fixed, your job is not quite over. Retest it with your original steps, confirm the actual result now matches the expected one, and close the loop on the ticket. If your team has a QA function, the QA to developer handoff checklist covers what each side owes the other from triage to verification.
What to do next
Take the last bug report you filed and read it as if you had never seen the product. Could you reproduce it from the text alone? Is there a line labelled Expected and a line labelled Actual? Does the title say where and what? Fix the one field that is weakest, and make that field the thing you check first next time.
If you test for a living, keep a short recording habit too. A thirty second clip of the bug happening, linked in the ticket, settles most "can't reproduce" threads before they start. The screen recorder for QA page shows how VeoRec fits into that, and the templates post has a version of this structure for every kind of bug you are likely to meet.
Frequently asked questions
What should a bug report include?
A specific title, the environment (browser, OS, device, account and build), numbered steps to reproduce from a clean start, the expected result and the actual result, evidence such as a screenshot, recording or exact error text, and a severity with a one-line reason. Add frequency: every time, sometimes, or once.
How long should a bug report be?
As long as it takes for a stranger to reproduce the bug, and no longer. For most web app bugs that is a title, three to eight steps, two result lines and a link to evidence. Long background stories belong in a comment, not in the steps.
What is the difference between bug severity and priority?
Severity describes the impact on users: data loss, a blocked task, a cosmetic flaw. Priority describes when the team will fix it, which also depends on deadlines, effort and other work. Reporters usually set severity; whoever plans the work sets priority.
Should I attach a screenshot or a video to a bug report?
Use a screenshot when the problem is visible in a single frame, such as wrong text or a broken layout. Use a short recording when the bug needs a sequence of actions or depends on timing, such as a double submit or a flicker. In both cases, keep the written steps; evidence supports them but does not replace them.
What if I cannot reproduce the bug again?
File it anyway if the impact was real, and say clearly that it happened once. Include the time it happened, your account, the URL and anything you remember doing, so a developer can look for the request in the logs. Then note any later attempts and their results on the same ticket.