Writing Bug Reports Developers Actually Act On
"It doesn't work" is not a bug report.
Neither is a Loom recording with no description, or a Slack message with a screenshot and no context. These artifacts require a developer to do 20 minutes of detective work before they can even understand what they're looking at, let alone start fixing it.
The bug reports that get fixed fastest share a common structure: they make the developer's job easy. They answer every question before the developer has to ask it. They are written with the reader in mind.
This is a skill, and a learnable one.
The Anatomy of an Excellent Bug Report
Every effective bug report answers the same questions:
mindmap
root((Bug Report))
What Happened
Actual behavior observed
Error message verbatim
Screenshot or recording
What Should Have Happened
Expected behavior
UX spec reference if applicable
How to Reproduce It
Preconditions account state
Steps numbered and exact
Data inputs used
Environmental Context
Browser and version
OS and device
User role and plan
Feature flags active
Severity and Priority
User impact quantification
Workaround available?
Frequency reproducible vs intermittent
The Bug Report Template
Use this as a starting point and adapt to your tool of choice:
## Bug Summary
[One sentence: who does what and what breaks]
Example: "Pro users cannot export scan reports to PDF on Safari 17"
## Environment
- **URL:** https://app.scanlyapp.com/reports/[report-id]
- **Browser:** Safari 17.2.1
- **OS:** macOS 14.2 (Sonoma)
- **User role:** Pro plan (not Trial, not Enterprise)
- **Account:** qa-test@example.com (use test account, not real user)
- **Feature flags:** pdf-export-v2=enabled
## Steps to Reproduce
1. Log in as a Pro plan user
2. Navigate to Reports → Any completed scan report
3. Click "Export" → "Download PDF"
4. Observe the download behavior
## Expected Behavior
A PDF file named `scanly-report-[date].pdf` downloads, containing the full scan report.
## Actual Behavior
The download dialog appears for ~2 seconds, then disappears. No file is downloaded.
Console error: `TypeError: Cannot read properties of undefined (reading 'buffer')`
## Evidence
- [Screenshot: Download dialog appearing](link)
- [Screen recording: Full reproduction](link)
- [Browser console output](link)
## Frequency
100% reproducible on Safari 17+ (tested on 2 different Macs).
Does not reproduce on Chrome 120, Firefox 121, or Edge 120.
## Severity
**P2 - High**
- Affects all ~340 Pro plan users who use Safari (estimated 23% of Pro users based on analytics)
- Workaround: Use Chrome for exports
- No data loss; purely functional
## Additional Context
This feature worked in Safari 16. The regression likely relates to the `pdf-export-v2` flag
rolled out in the 2026-01-15 deployment.
Related issues: #456 (initial PDF export implementation)
Severity vs Priority: Not the Same Thing
This confusion causes misaligned expectations between QA and engineering:
| Severity | Definition | Example |
|---|---|---|
| Critical (S1) | Data loss, security breach, complete service down | Payment charged but subscription not activated |
| High (S2) | Core feature broken with no workaround | Cannot create new project |
| Medium (S3) | Feature degraded, workaround exists | Sorting broken on report table |
| Low (S4) | UI cosmetic, minor UX issue | Button alignment off by 2px |
| Priority | Definition | Example |
|---|---|---|
| P1 | Fix immediately, can block release | S1 bug in production |
| P2 | Fix before next release | S2 affecting significant users |
| P3 | Fix in current sprint | S3 with no workaround |
| P4 | Backlog, fix when time permits | S4 cosmetic issues |
An S2 issue might be P4 if it affects a deprecated feature with 3 users. A low-severity S4 cosmetic issue on the signup page might be P2 because it erodes trust in a high-visibility conversion flow.
Writing for Velocity: The Things That Slow Down Bug Resolution
| Anti-Pattern | What to Do Instead |
|---|---|
| "It's broken" | Describe exactly what breaks |
| No reproduction steps | Write numbered steps, assume nothing |
| "I saw this happen once" | State exact frequency and conditions |
| Screenshot of the wrong thing | Screenshot the exact error or broken element |
| Missing environment info | Always include browser, OS, user role |
| Priority: Critical (for everything) | Reserve Critical for actual data loss / security |
| No expected behavior | State exactly what should happen |
| "The new feature broke something" | Link to the specific change if known |
| Missing test account credentials | Include test account details (or note they exist in 1Password) |
The Multi-Component Bug: Distributed System Failures
For SaaS applications with frontend, API, queue workers, and databases, bugs often span layers. A multi-component bug report adds one section:
## Affected Components
- ✅ Frontend: UI correctly submits request
- ✅ API: Returns 200 with job ID
- ❌ Worker: Job fails with "connection timeout to S3"
- ✅ Database: Job record created, status never updated
## What I've Checked
- Worker logs show Redis RPOPLPUSH timing out at exactly 30s
- This does NOT occur on staging (different Redis tier)
- Started occurring after the 2026-02-10 deployment
This section saves the developer 30 minutes of log diving to identify which system component to look at first.
Related articles: Also see making the block decision the evidence in your bug report supports, applying bug report discipline to findings from a company-wide bug bash, and escalating bug patterns into the QA dashboards that drive decisions.
Automating Bug Report Quality
For teams using Jira or Linear, enforce required fields with custom workflows:
// .github/ISSUE_TEMPLATE/bug_report.yml
name: Bug Report
description: File a bug report
labels: ["bug", "needs-triage"]
body:
- type: input
id: summary
attributes:
label: Bug Summary
description: One sentence describing what breaks
validations:
required: true
- type: dropdown
id: severity
attributes:
label: Severity
options: ["S1-Critical (data loss/security)", "S2-High (core feature broken)", "S3-Medium (workaround exists)", "S4-Low (cosmetic)"]
validations:
required: true
- type: textarea
id: steps
attributes:
label: Steps to Reproduce
placeholder: "1. \n2. \n3. "
validations:
required: true
- type: input
id: environment
attributes:
label: Environment (browser, OS, user role)
validations:
required: true
Good bug reports are a form of documentation. They create a precise record of what was wrong, what was expected, and what was done to reproduce it. This record is valuable long after the immediate fix for understanding regressions, informing test case design, and tracking patterns in defects over time.
Catch bugs automatically before they need a bug report: Try ScanlyApp free and run automated checks that surface issues in your app before your users do.
