How to Write a Good Functional Bug Report

When I started in QA, I thought I had bug reports figured out. Steps to reproduce. Expected result. Actual result. Screenshot. Every tutorial said the same thing, so I did the same thing. Felt solid.

Then I joined a company that used freelance testers. Video attachments were mandatory — not recommended, mandatory. The reasoning was blunt: you can’t fake a video. A screenshot proves nothing about how you got there.

In hindsight it was so obvious and made so much sense. I have never gone back.

A bug report is a communication artifact

Most tutorials treat bug reports like a form to fill out. They’re not. A bug report is how you communicate a risk to the people who need to act on it. The format isn’t arbitrary — it should follow the type of risk you’re describing.

A functional bug tells a different story than a performance bug. A performance bug needs different information than an accessibility bug. If you’re writing every bug report the same way regardless of type, you’re probably leaving out something that would help someone make a decision, or including stuff that wastes their time.

The anatomy of a good functional bug report

Most bugs you file will be functional — something the application does that it shouldn’t, or doesn’t do that it should. Here’s what I’ve found actually works.

Title. Short, but specific. I think of it as three parts: the feature area in brackets, what happened, and where, and how it happened. Something like: “[Reserve] Selecting a flight does not navigate to the purchase form on the reserve page.” Anyone scanning a board can understand it without opening the ticket.

Summary. One or two sentences at the top for the person who won’t read the rest. Just describe the bug plainly. Developers are busy. Give them the short version up front.

Risk Level. I think of this as two things: how likely are end users to hit the bug, and how bad is it for the user if they hit the bug. Those two together tell the team where it sits in the queue. A bug that’s catastrophic but buried in a rarely-used edge case is a different conversation than something that breaks the happy path for everyone.

High LikelihoodLow Likelihood
High ImpactCriticalHigh
Low ImpactMediumLow

Critical — High impact, high likelihood. The bug blocks or seriously harms the user and they will definitely hit it. Anything on the happy path that prevents a user from completing their goal. Example: clicking “Choose This Flight” does nothing. Every user booking a flight hits it.

High — High impact, low likelihood. Serious consequences but harder to trigger. Example: payment fails but only when using a specific card type on a specific browser.

Medium — Low impact, high likelihood. Everyone sees it but it’s not catastrophic. Example: the flight price on the reserve page shows two decimal places inconsistently.

Low — Low impact, low likelihood. Minor and hard to hit. Usually deferred. Example: a label is misaligned on a confirmation screen that only appears after a specific promo code is applied.

These are guidelines, not rules. Every team weighs risk differently depending on the product, the users, and the business. A payment bug at a fintech company hits differently than the same bug on a demo site. Use the matrix as a starting point and calibrate it to your team.

Steps to reproduce. Write them, but know their limits. Steps can be misread. A developer following your steps will make different assumptions than you did. Words can be ambiguous.

Actual and expected results. Still necessary. Still useful. Just not sufficient on their own.

Video. This is where most bug reports fall short. A video of the steps being performed removes ambiguity. The developer can see exactly what you clicked, in what order, at what speed. They can reproduce it more reliably because they’re following what you actually did, not their interpretation of what you wrote.

Keep it short. Under 30 seconds if the bug allows it. If you need a longer clip, add a timestamp note — something like “bug occurs at 0:43” — so the developer can scrub straight there instead of watching you navigate for five minutes. You want to walk through the bug with others efficiently, and not just dump evidence on them.

If the bug triggered a failed network request, attach the logs. On web, browser network logs. On mobile, Charles Proxy or equivalent. A broken UI with a failed endpoint behind it can pinpoint to the developer the exact file or line of code to investigate.

Environment. Always include the release version — this is how you trace regressions. On mobile, device model and OS version matter because device-specific bugs are common. On web, I include browser info, but honestly it depends on your team. If you’re not doing cross-browser testing, adding the browser just creates noise nobody acts on.

That last point is worth sitting with. More information isn’t always better. Every field you add costs someone time — yours to fill it, the developer’s to read it. Include what your team can actually use. Leave out what they can’t.

Performance and accessibility bugs need different information

Performance bugs and accessibility bugs follow the same idea — the format serves the risk — but they each deserve more than a paragraph. I’ll cover each one separately.

Here’s the short version for now. Performance bugs without numbers aren’t really bug reports. “The page is slow” tells nobody anything. You need: what slow means (response time, load time, a specific metric), under what conditions (load, device, network), and ideally compared to what baseline. The engineering team needs to understand the threshold being crossed, not just that something felt sluggish.

Accessibility bugs need to name the specific failure. Which WCAG criterion is violated. What assistive technology was used and how — screen reader, keyboard-only navigation, zoom level. What the user experience actually is, not just that a screen reader announced something weird. Product and engineering teams often have less context here, so the report needs to do more work to explain the impact.

More on both of those soon.

An example

Here’s a fictional bug filed against BlazeMeter’s demo travel site, which is a common practice app in the testing world.


Title: [Reserve] Selecting a flight does not navigate to the purchase form on the reserve page

Summary: After searching for flights, clicking “Choose This Flight” on the reserve page does not redirect the user to the purchase form. The page reloads but stays on the reserve page. The user cannot complete a booking.

Risk Level: Critical

  • Impact: High — blocks the core booking flow entirely. Users cannot complete a purchase.
  • Likelihood: High — this is the happy path. Every user who attempts to book a flight will hit it.

Steps to reproduce:

  1. Go to blazedemo.com
  2. Select “Boston” as departure city and “London” as destination city
  3. Click “Find Flights”
  4. On the reserve page, click “Choose This Flight” on any listed flight

Actual result: The page reloads and remains on the reserve page. No navigation to the purchase form occurs.

Expected result: User is redirected to blazedemo.com/purchase.php to complete the booking.

Attachments:

Video (see below) — bug occurs at 0:15

Network log (see attached) — POST to /reserve returns a 500 error

Environment:

  • Release: v2.4.1
  • Browser: Chrome 124
  • OS: macOS 14.4

Notice how every field serves someone. The title and summary are for anyone scanning the board. The risk level is for product deciding what to prioritize. The steps and video are for the engineer and tester reproducing it. The network log tells them where to start looking. The environment is for whoever needs to trace it back later. Nothing in there is filler.

The hard part

Filing the steps is simple if not tedious. The hard part is remembering what you’re actually doing — communicating a risk to people who need to act on it. Every field in a bug report is a chance to make that risk clearer or murkier. A vague title, a missing video, a risk level with no reasoning — each one is a small failure of communication that compounds. The bug might get fixed eventually. But a good report makes sure the right people understand the right risk at the right time.

  • May 11, 2026