Everything rests on the reproduction steps
A bug report has one job: put the defect on an engineer's screen. Environment, severity, attachments — all of it is scaffolding around that job. If the steps reproduce, a mediocre report is still a fixable ticket. If they don't, an immaculate report is a research project with a due date.
"Search is broken" costs an engineer an hour before they can start. They have to guess which search, on which screen, signed in as whom, against what data — rebuilding a state the reporter already had open in front of them. Then, often enough, they can't reproduce it, close it Cannot Reproduce, and the same defect is filed again five weeks later by somebody else, in the same three words.
What a step that actually reproduces looks like
- Start from a named state. "Signed in as a user with the Viewer role, on a project with more than 200 issues." Not "open the project." Most bugs live in the precondition, not in the click — the role, the data volume, the migrated account.
- One action per step. If a step contains the word "and", it is two steps, and you have given up the ability to say which half broke.
- Real values, not placeholders. The 500-row CSV. The display name with Cyrillic characters. The due date in a timezone a day ahead. The sample values on the format above read that way on purpose: "Paste a 500-row CSV and click Import" reproduces, "import some data" does not.
- Stop at the failure. Three steps that end where it breaks beat eleven that wander past it.
Actual and expected are two fields on purpose
A large share of filed bugs are not defects. They are disagreements about intended behaviour, and the split between Actual Result and Expected Resultturns those into a two-minute product decision instead of a week of code archaeology. With "expected" blank, nobody downstream can tell a defect from a feature request. When it contradicts the spec, you have found a documentation bug — the cheapest kind there is.
Two bug report formats, because there are two reporters
The person who finds a bug is usually not the person who fixes it. A support agent has a customer on the line and four minutes. A PM saw it on a demo laptop. A customer is describing it from memory. None of them can open a console or name your build number.
The reporter format asks for OS, browser, app version, environment, three steps, actual, expected, severity and evidence — every one answerable by somebody with no repository access. The engineering variant below assumes more: severity as checkboxes, a console-errors block, a user account, and a regression tri-state — this was working before / this never worked / unknown.
That tri-state is the highest-value field on either form and the one most often left blank. "This was working before" converts an open-ended investigation into a bisect across a known range of commits. "This never worked" says stop hunting a regression and go read the original ticket.
Hand the engineering form to a support agent and you get four empty sections and a ticket that reads worse than the one you would otherwise have had. Hand the reporter form to your own engineers and you lose the console output that would have named the failing call. Keep both, and let triage promote a reporter-filed bug into the engineering format once it reaches someone who can fill the extra fields.
Severity is how badly it breaks. Priority is when you fix it.
This is the most expensive confusion in bug triage, and most Jira instances make it worse by shipping one Priority field that people quietly use for both. Severity is an observation about the software; QA or the reporter sets it. Priority is a decision about the schedule; a PM or a tech lead sets it. What separates the engineering variant's four severity levels in practice:
- Critical — system down or data loss. The test is whether you would wake someone at 2am. Data that is silently wrong counts, and is worse than data that is visibly gone.
- High — a primary flow is broken with no acceptable workaround. People can still work; they cannot do this.
- Medium — impaired, workaround exists. The level hangs entirely on that second clause: if you can tell a customer what to do instead and they can do it, it is Medium.
- Low — cosmetic or a minor inconvenience. The data and the outcome are correct.
Priority runs on a different axis. A misaligned button on the signup page is Low severity and top of the sprint, because every prospect sees it. A data-corruption bug in an export nobody has run since March is Critical severity and can wait for the next release. Keeping both fields is what lets you say that out loud. If your project has room for only one, pick priority and record severity in the description — then never argue that a Low-priority bug isn't serious.
The test case is the proof, and it belongs to the bug
A fix without a test case is a fix that regresses. The bugs that come back a third time are, without exception, the ones nobody wrote a test for.
The test case format below carries an objective, preconditions, a step/action/expected table, an actual result and a status. The table earns its keep: one row per assertion means a failure names its own row. "Failed at step 4" is something an engineer can act on. "Test failed" is not.
Worth writing when the bug has already come back once, when the path is expensive to reach by hand — a specific tenant configuration, a state that takes ten minutes to set up — or when the behaviour is a contract somebody else builds against: an API response shape, an exported file format, a permission boundary.
Ceremony when it covers a typo fix, restates a unit test that already exists, or describes code being deleted next sprint. The honest check is whether anyone will ever run it again. If not, you have written documentation with a table in it.
Test cases are not acceptance criteria, and the two get conflated constantly. Criteria describe what a story must do before it is built and belong to the product. A test case describes how you prove it after, and belongs to QA. For the criteria half, the Jira user story guide covers it.
The QA checklist is a release gate, and gates fail at 5pm on Fridays
The QA checklist below is not a bug format. It runs once per release across four bands — functional, cross-browser, regression, non-functional — and ends with two sign-off lines carrying names and dates. Those two lines are the entire mechanism. A checklist with no name on it is a wish, and one signed by "the team" is signed by nobody.
Then it fails at 5pm on a Friday with the release announced for Monday. The interesting question is not whether you ship. It is whether anyone decided that you would, on the record. Write the rule before you need it: name who can waive a line, require the waiver to be a comment naming which line and why, and file the unchecked item as a bug with a date. Teams without that rule do not stop shipping on Fridays. They stop writing down that they overrode the gate.
The tension does not resolve. A gate never overridden is either too weak to catch anything or attached to a team that has stopped shipping. A gate overridden every time is theatre with a checkbox. Track how many lines were waived rather than how many were ticked, and which lines keep appearing — those are either wrong or genuinely dangerous, and both answers are useful.
Gherkin earns its overhead, or it is theatre
Given/When/Then costs several times more to write than a bulleted list of criteria. Two things pay that back, and only two.
- Something executes it. Cucumber, SpecFlow, Behave, pytest-bdd — step definitions bind to code and the scenario is the test. The rigid grammar exists because a parser reads it.
- Someone outside engineering reads it. A domain expert, a compliance reviewer, a customer signing off on behaviour. The stiff phrasing is what lets a non-engineer catch a wrong rule before it costs a sprint.
It is theatre when an engineer writes Given/When/Then after the code merged so the ticket looks complete at sprint review. Nothing runs it, nobody outside the team opens it, and it is a second description of the implementation that will drift within two sprints and then actively mislead the next reader — worse than no scenario at all. Before writing the Gherkin scenarios below, name the runner that executes them or the person who reads them. If you can name neither, write plain criteria and spend the time on the test case.
Triage is a process, not a format
These five formats fix the shape of a bug. None of them decides what happens to it. That gap — between filed and assigned — is triage, and it is a standing slot on someone's calendar, not a field on a form. Working triage does four things per bug, in a couple of minutes each:
- Confirm it reproduces, or bounce it back with the one question that would let you.
- Set severity, from the observed behaviour.
- Set priority, against what else is in the sprint.
- Route it — assign, or close as duplicate, won't fix, or works as intended.
The fourth is the one teams skip, and skipping it is how you get to 400 open bugs. A 40-bug backlog is a to-do list; somebody has read all of it this quarter. A 400-bug backlog is a landfill — nobody reads to the bottom, duplicates get filed weekly because searching is slower than refiling, and the twelve bugs in there that would change a customer's mind are invisible. The count is not the problem; the 360 nobody has opened in a year are. Closing bugs you will not fix is not dishonest bookkeeping — it is the only way the ones you will fix stay findable.
When you don't need any of this
Three people sitting in one room do not need a five-format QA apparatus. A Slack message with a screenshot and a failing test is the entire process, and bolting a severity scale onto it slows the team down without making anything more reliable.
These formats start paying for themselves at four thresholds:
- The reporter is not the fixer — support, sales or customers file bugs.
- There are more open bugs than one person can hold in their head, which in practice is around thirty.
- You ship on a date other people have planned around.
- An auditor or an enterprise customer asks how you verify a release.
Adopt them in that order. The reporter's bug report first, because the top of the funnel is where information is lost and never recovered. Triage second. The release checklist the first time a release breaks something that used to work. Gherkin last, and only if something executes it.