How to Write a Perfect Bug Report — Template, Fields, and Examples
A bug report is a structured record of a software defect that gives an engineer everything they need to reproduce, diagnose, and fix an issue without follow-up questions. A complete report has eight fields — title, environment, description, steps to reproduce, expected vs actual behaviour, severity and priority, attachments, and additional context — and the template below covers every one. Copy it into Jira, Linear, GitHub Issues, or ClickUp and your team's average bug-to-fix time drops on the same day you start using it.
Key takeaways
- A perfect bug report answers four questions in order: what broke, where, how to reproduce it, and what should have happened instead.
- Severity describes technical impact; priority describes business urgency. Mixing them up is the single most common triage mistake — we cover the distinction below and link out to a deeper breakdown.
- The IEEE 1044-2009 classification standard, while inactivated in 2020, still anchors the four severity tiers (Critical, High, Medium, Low) that every major tracker uses today.
- The fields most reporters skip — console logs, network requests, screen recordings — are the fields developers need most. Tools like the Crosscheck Chrome extension attach all of them in one click.
- The template at the bottom of this guide is plain markdown, works in any tracker, and is free to copy.
What makes a bug report "perfect"?
Useful, not exhaustive. A perfect bug report is one a developer can act on without sending a single follow-up message. That standard sounds modest until you measure how often it fails — informal tracker audits inside Crosscheck's own customer base routinely show that between 30% and 50% of bug tickets bounce back at least once for clarification before any code is written.
Three properties separate reports that move quickly from reports that stall:
- Reproducibility. Anyone on the team, given the report and access to the right environment, can recreate the bug within a few minutes.
- Evidence. The technical context — screenshot, recording, console error, failing network response — is attached, not described in prose.
- Disambiguation. Severity is separated from priority, expected behaviour is separated from actual behaviour, and the title states the symptom in language an engineer can pattern-match against the codebase.
Reports that hit those three notes get triaged once and fixed. Reports that miss any of them generate a Slack thread, a follow-up question, a "cannot reproduce", and eventually a stale ticket that re-emerges as a production incident a quarter later.
The 8 fields every bug report needs
Different trackers expose different schemas — Jira lets you build custom screens per work type, Linear uses a flatter model centred on labels and a 0–4 priority integer, GitHub Issues stores everything inside bug_report.yml form blocks — but the underlying fields a fixer actually reads are remarkably consistent. Here they are.
1. Title
The title is read more than any other field. It shows up in the backlog list, the triage queue, the Slack notification, the release notes, and (if it survives that long) the postmortem. Write it like a single-line bug summary an engineer could grep against the codebase.
- Bad:
Login broken - Good:
Login form returns 200 but does not redirect to /dashboard — user stays on /login
Specific verbs, specific symptoms, specific surface. Adjectives like "broken", "weird", and "not working" carry no information and should never appear in a title.
2. Environment
Bugs are environment-shaped. The same payload that 500s in production may pass in staging because feature flags differ, because a database migration ran on one but not the other, or because the bug only surfaces on Safari 17 with reduced motion enabled. Capture enough that a reproduction attempt on the wrong environment cannot waste anyone's afternoon.
A complete environment block lists browser and version, operating system, application version or commit SHA, deployment environment (production, staging, preview), device type, screen resolution where relevant, and the user role or account type the bug was observed under.
3. Description
A two-to-four sentence framing paragraph. Not the steps, not the evidence — just enough plain prose for someone reading the ticket cold to understand what was being attempted and what went wrong. A good description gives the engineer a hypothesis before they hit "reproduce".
- Bad:
When I click login nothing happens. - Good:
After submitting valid credentials, the Sign In button briefly shows a spinner. The /auth/login endpoint returns a 200 with a valid session cookie, but the client never navigates away from /login. Likely a client-side routing regression rather than an auth failure.
4. Steps to reproduce
The most-skipped, most-important section. Numbered, atomic, specific. Each step is one action with one observable result. Start from a precondition state — "user is logged out, cart is empty" — and end with the action that produces the bug.
A useful test: hand the steps to someone who has never seen the product. If they cannot reproduce the bug in under two minutes, the steps are not complete.
5. Expected vs actual behaviour
Two short fields, side by side. Expected is what the spec, the design, or the user reasonably anticipated. Actual is what the system did. The gap between them is the bug — and writing both out explicitly forces the reporter to articulate the contract, not just the symptom.
The pair also doubles as the implicit acceptance test for the fix. When actual === expected, the bug is closed.
6. Severity and priority
Severity and priority answer different questions and should never share a field. Conflating them is the single largest source of mis-triaged tickets we see.
| Field | Question it answers | Owned by | Example value |
|---|---|---|---|
| Severity | How badly does the bug break the product? | QA / reporter | Critical, High, Medium, Low |
| Priority | How urgently does the business need it fixed? | Product / triage | P1, P2, P3, P4 |
The IEEE 1044-2009 classification standard — the canonical reference here, even after its 2020 inactivation — was explicit that severity is a mandatory impact category and priority is a separate, subjective ranking that combines severity with cost, schedule, customer value, and risk. Modern trackers reflect that split: Jira exposes both fields out of the box on its Bug work type, Linear exposes priority as a 0–4 integer (0 No priority, 1 Urgent, 2 High, 3 Medium, 4 Low) and recommends labels like severity-critical for the impact axis. GitHub Issues leaves both to labels.
A common, useful default scale:
Severity
- Critical — application crash, data loss, security exposure, complete feature failure with no workaround.
- High — major feature broken, significant user impact, no reasonable workaround.
- Medium — feature partially broken, workaround exists but is inconvenient.
- Low — cosmetic issue, edge case, minimal user impact.
Priority
- P1 — fix immediately, hot-patch if in production.
- P2 — fix this sprint.
- P3 — schedule in the near-term backlog.
- P4 — fix when convenient.
A cosmetic typo on a checkout button during Black Friday is Severity: Low, Priority: P1. A crash in a quarterly admin export used by three internal users is Severity: Critical, Priority: P3. Keeping the fields separate makes both decisions defensible. For a deeper treatment, see Crosscheck's primer on SQA methodologies and real-world case studies.
7. Attachments — screenshots, recordings, console logs, network logs
The field where most bug reports collapse. A paragraph describing a UI glitch is a poor substitute for a screenshot. A paragraph describing a failed API call is a worse substitute for the request and response. Every additional layer of evidence cuts diagnosis time, and gathering that evidence is exactly the activity reporters avoid when they are tired, in a hurry, or unfamiliar with browser DevTools.
The four pieces of evidence that matter:
- Screenshot — capture the full browser window so the URL, page state, and visual context are all visible. Crop later if needed.
- Screen recording — the right format for any bug involving a sequence of actions, timing, or intermittent behaviour. A 30-second clip removes the entire "I can't reproduce this" loop.
- Console logs — JavaScript errors, warnings, deprecation notices. Many frontend bugs are invisible in the UI but loud in the console. Paste the relevant lines into the ticket or attach the full log.
- Network logs — request URL, method, status code, request and response headers, response body, timing. For bugs that involve "the page didn't load the right data", network logs are usually the answer.
The friction of collecting all four manually is the reason reporters skip them — and the reason tools like the Crosscheck Chrome extension exist. One click captures the screenshot, the recording, the console log, and the network requests, then sends them to Jira, Linear, ClickUp, GitHub, or Slack with the reporter's title and steps already attached.
8. Additional context
A catch-all for the information that does not fit anywhere else but might tip the diagnosis: frequency (always vs intermittent, with an estimated rate), first-observed timestamp (useful when correlating with a deploy), related tickets or PRs, known workaround, impact estimate, whether the bug existed in a previous version, and any guesses the reporter has about the root cause.
The rule of thumb — anything that would help the next engineer in the chain, no matter how speculative, belongs here. Anything that does not, does not.
Good vs bad: the same bug, two reports
The contrast below is composite but representative — both versions land in real backlogs every day.
Bad report
Title: Payment not working
Description: When I try to pay it doesn't go through. I've tried a few times.
Severity: High
Three sentences, no environment, no steps, no evidence, no expected behaviour, no priority distinction. The first developer to read this will write back asking what payment method, what browser, what error — and the reporter will have moved on to other work.
Good report
Title: Checkout payment fails for Visa cards — spinner runs indefinitely, no error shown, no order created
Environment: Chrome 124.0.6367.60, macOS 14.4, app v3.2.1, production, standard user role, desktop
Description: On
/checkout, after entering a valid Visa card and clicking "Pay Now", the button shows a loading spinner that never resolves. No error message appears in the UI. The order is not created (confirmed via the admin orders list). The browser console logsTypeError: Cannot read properties of undefined (reading 'orderId')immediately after thePOST /api/ordersresponse returns. Mastercard and Amex appear unaffected in spot checks.Steps to reproduce:
Preconditions: logged in as a standard user, cart contains at least one product.
- Navigate to
/checkout.- Fill in a valid shipping address.
- Enter Visa card
4111 1111 1111 1111, expiry12/26, CVV123.- Click "Pay Now".
- Wait 60+ seconds — observe spinner does not resolve.
- Open DevTools → Console. Observe
TypeError: Cannot read properties of undefined (reading 'orderId').- Open DevTools → Network. Observe
POST /api/ordersreturns 200 with body{ "status": "ok" }but noorderIdfield.Expected: User is redirected to
/order-confirmationwith a new order ID; the order appears in the admin list.Actual: Spinner runs indefinitely, no redirect, no error message, no order created.
Severity: Critical — Visa is roughly 60% of card volume; checkout is fully blocked for that segment.
Priority: P1 — production, no workaround, revenue impact.
Attachments: 45-second screen recording, full console log, HAR file of the failing
POST /api/ordersrequest.Additional context: Started after the v3.2.1 deploy at 09:14 UTC. Mastercard and Amex unaffected in the same flow. Likely related to PR #4821, which renamed the API response field from
order_idtoorderId— possibly only applied to one branch of the payment processor adapter.
The good report is longer, but the time a developer spends reading it is recovered ten times over in the investigation phase. The bad report consumes a Slack thread, a triage cycle, and an apology. The good report consumes one engineer-hour and a deploy.
A free bug report template you can paste into any tracker
Plain markdown. Works in Jira's wiki-style descriptions, Linear's markdown editor, GitHub Issues, ClickUp, Notion, Slack canvases, and email. Copy, paste, fill in.
## Bug report
**Title:** [What is broken, where, in one line]
---
### Environment
- Browser:
- OS:
- App version / build:
- Environment: production / staging / development
- Device:
- Screen resolution:
- User role / account type:
---
### Description
[Two to four sentences. What were you doing, what happened, why is this a problem?]
---
### Steps to reproduce
Preconditions: [the state the system needs to be in before step 1]
1.
2.
3.
4.
---
### Expected behaviour
[What should have happened.]
### Actual behaviour
[What did happen.]
---
### Severity
- [ ] Critical — crash, data loss, security, total feature failure
- [ ] High — major feature broken, no workaround
- [ ] Medium — partial failure, workaround exists
- [ ] Low — cosmetic, edge case
### Priority
- [ ] P1 — fix now
- [ ] P2 — fix this sprint
- [ ] P3 — fix soon
- [ ] P4 — schedule at convenience
---
### Attachments
- [ ] Screenshot
- [ ] Screen recording
- [ ] Console log
- [ ] Network log (HAR or copied request/response)
- [ ] Other:
---
### Additional context
- Frequency: always / intermittent (~X%) / one-off
- First observed:
- Related tickets / PRs:
- Workaround:
- Suspected cause:
- Notes:
Adapting the template per stack
Treat the template as a backbone, not a contract. Different stacks earn different fields.
Mobile. Add device model, OS build (not just version), network type (Wi-Fi, 4G, 5G), and battery state for performance issues. App-store build number matters more than semantic version.
API. Replace the click-by-click steps with a working curl command, the request body, and the full response — status, headers, body. If the bug requires a specific auth scope, include it.
Data. Capture before-and-after state. The ID of the affected record matters more than a screenshot. Note whether the data is reproducible in a fresh test account.
Performance. Add a "metrics" block: observed time-to-interactive, Lighthouse score, Core Web Vitals, network throttling settings, CPU throttling settings. Without those numbers, "the page is slow" is unfixable.
Accessibility. Add the WCAG criterion in scope (e.g. 2.1.1 Keyboard), the assistive technology used to surface the bug (NVDA, VoiceOver, JAWS, keyboard-only), and the WCAG conformance level the project targets (AA, AAA).
Security. Route the report through a private channel — never the public tracker. The structure is the same; the audience is not.
Wiring the template into Jira, Linear, GitHub, ClickUp
A template living in a wiki gets used once a quarter. A template wired into the issue creation flow gets used every time.
Jira. Build a Bug work-type screen with the eight fields exposed in the order above. Atlassian rolled out field-scheme limits in February 2026 — 700 fields per configuration, 150 work types per scheme — so prune unused fields aggressively. Use a default-value template in the description field if your edition supports it, or install a free template add-on if not.
Linear. Linear has no native severity field. Map severity to labels (severity:critical, severity:high, …) and keep priority on the built-in 0–4 integer. Use Linear's issue templates to pre-populate the description with the markdown template above.
GitHub Issues. Create .github/ISSUE_TEMPLATE/bug_report.yml. Use textarea blocks for the description, steps, expected, and actual fields, a dropdown for severity and priority, and checkboxes for the attachment list. The YAML form schema converts cleanly to a structured issue body on submission.
ClickUp. Create a "Bug report" task template with custom fields for severity and priority. Set required fields where the schema supports it.
In every case the same principle applies — make the right thing the easy thing. If filing a high-quality bug report takes a minute longer than filing a vague one, vague ones will win.
The fields people skip — and why automation matters
If you audit any team's tracker for a week, the four fields skipped most often are the four most useful: screen recording, console logs, network logs, and exact environment. They are skipped because gathering them is genuinely tedious — open DevTools, switch tabs, copy the right log lines, export the HAR, save the screenshot, attach four files, type a sentence summarising what is in them.
The Crosscheck Chrome extension was built for exactly that gap. A single click captures the screenshot, the screen recording (with the rage-clicks and timing intact), the full console log, and every network request from the session, then attaches them to a bug ticket in Jira, Linear, ClickUp, GitHub, or Slack. The reporter still writes the title, description, and steps — those need a human — but the evidence layer fills itself.
The downstream effect is what teams notice first. The "can you reproduce this and send me your network log?" Slack thread disappears. Triage gets shorter. The "cannot reproduce" close rate falls. Reports that used to require three rounds of clarification land complete on the first try.
For a wider view of the tooling landscape, Crosscheck maintains a comparison of the best bug reporting tools in 2026 and a separate guide to becoming a QA engineer that covers tooling fluency as a hiring signal.
FAQ
What is the difference between a bug report and an issue?
A bug report is a specific class of issue — one that documents observed incorrect behaviour against a working specification. An issue is a broader category that includes bugs, feature requests, tasks, chores, questions, and discussion threads. In Jira, "Bug" is one work type alongside Story, Task, Epic, and Sub-task. In Linear and GitHub, "issue" is the catch-all and bugs are usually distinguished by label. Most engineering teams use both terms interchangeably in conversation and rely on the tracker's labels or work type to enforce the distinction.
How detailed should a bug report be?
Detailed enough that an engineer with no prior context can reproduce the bug in under two minutes and form a diagnostic hypothesis without asking a question. Past that point, extra detail is welcome but not required. The eight fields above are a useful upper bound for most product bugs; you rarely need more, and trimming below five fields (title, environment, steps, expected, actual) starts to cost more time downstream than it saves upstream.
What tools auto-generate bug reports?
A small but growing category. The Crosscheck Chrome extension captures screenshots, screen recordings, console logs, and network requests in one click and sends a structured report to Jira, Linear, ClickUp, GitHub, or Slack. Marker.io and Jam.dev sit in adjacent niches. Mabl's Auto TFA (Autonomous Test Failure Analysis), shipped in early 2026, auto-generates root-cause-annotated tickets from failing test runs. Sentry and Datadog turn production errors into draft tickets with stack traces pre-populated. None of these tools replaces the title and reproduction steps a human writes — they fill the evidence layer underneath.
Is severity the same as priority?
No, and treating them as the same is the most common bug-triage mistake. Severity is the technical impact of the bug — how badly the product is broken. Priority is the urgency of fixing it — how soon the business needs it resolved. A typo on a high-traffic page can be low-severity, high-priority. A crash in a quarterly admin export can be high-severity, low-priority. The IEEE 1044-2009 classification standard codified the split, and every major issue tracker — Jira, Linear, GitHub — supports both as independent fields or labels.
Can AI write bug reports for me?
For the title, description, and additional-context fields, AI assistants are useful as a draft pass when paired with captured evidence — they can summarise a console log into a one-line title or translate a HAR file into prose. They cannot replace steps to reproduce (those need a human who actually triggered the bug) and they should never invent severity or priority (those are business calls). Treat AI as the editor, not the reporter.
What's the shortest acceptable bug report?
A title, a one-line description, the environment, three reproduction steps, expected, actual, and one piece of evidence (screenshot or recording). That is roughly the floor at which a report is still actionable on the first read. Below that, the engineer asks a follow-up; above that, you are paying with the reporter's time for diminishing diagnostic returns.
File your next bug with Crosscheck
If the eight fields in this guide describe the bug report you want, Crosscheck is the fastest way to file one. The Chrome extension captures screenshot, screen recording, console log, and network requests in one click, then ships the report to Jira, Linear, ClickUp, GitHub, or Slack with the template already structured. It is free, has no usage limits, and installs in under a minute.



