Why Bugs Get Reopened, and How to Cut the Rate

Written By  Crosscheck Team

Content Team

July 8, 2026 9 minutes

Why Bugs Get Reopened, and How to Cut the Rate

Why bugs get reopened, and how to cut the rate

BUG-3120 was closed on Tuesday. On Thursday a tester reopened it. The developer's comment: "Works on my machine, the fix is in." The tester's comment: "Still broken on staging."

Both were right. The fix worked for the case the developer read. It did not work for the case the tester meant. Nobody wrote that case down.

Short version

  • A reopened defect is a bug that was marked fixed, then found to be broken again during verification or later.
  • Four causes explain most reopens: vague acceptance, partial fixes, missing regression cases, and environment mismatch.
  • Reopens cost roughly twice the original fix, because two people context-switch back into work they had finished.
  • A reopen rate of 5 to 10 percent is normal. Above 15 percent means something in the handoff is broken.
  • The cheapest fix is writing the pass condition into the ticket before the developer starts.
  • Track why each bug was reopened, not just how many. The count alone tells you nothing you can act on.

What counts as a reopen

Be precise, or your number will mean nothing. A reopen is a ticket that moved from a closed or resolved state back to an open state because the reported problem still happens.

These do not count:

  • A new bug found in the same area. That is a new ticket.
  • A ticket reopened for a documentation or comment change.
  • A ticket reopened because the requirements changed after the fix.

Split those out with a "Reopen reason" field. If everything gets counted as a reopen, the metric becomes noise, and people start arguing about the number instead of fixing the cause.

Cause 1: the acceptance was never clear

This is the biggest one. The bug says what is broken. It does not say what "fixed" looks like.

Vague: "Discount code not applied correctly at checkout."

Clear: "With code SAVE20 on a 100 USD cart, the order total must show 80.00 USD before tax. Currently shows 100.00 USD. Fixed means the total updates within one second of applying the code, and the discount line appears in the summary."

The second version gives the developer a test they can run themselves. It also gives the tester the exact check to repeat. There is no room for two readings.

How to fix it

Add one line to every bug template: Fixed means:. Write it as a single observable statement with real values in it. Numbers, strings, and URLs beat adjectives.

Weak pass conditionStrong pass condition
Page loads fasterInvoice list renders in under 2 seconds with 500 rows
Error message is betterShows "Card declined. Try another card." instead of ERR_5012
Login works[email protected] can sign in on Safari 18 without a second prompt
Layout is fixedSidebar stays 240px wide at 1280px viewport width, no overlap

Cause 2: partial fixes

The developer fixed the path you described. The bug lives on three other paths.

A user reports that the export button fails on the invoice list. The developer fixes the invoice list. The same export component is used on the customer list and the payments list, and both are still broken. The tester checks the customer list next week and reopens the ticket.

How to fix it

Ask two questions before the fix is closed:

  1. Where else is this code used? If the fix touched a shared component, name every screen that uses it in the ticket.
  2. What was the actual cause? A one-line answer is enough: "The export helper assumed rows was always an array. It is undefined when the list is empty."

That second answer usually reveals the other paths on its own. If the cause is "empty list", every screen with a list is a candidate.

Ask the developer to paste the changed file paths into the ticket. A tester who can see that utils/export.ts changed will know to check every export in the product, not just the one they reported.

Cause 3: the regression case was never added

A regression is a bug that comes back after being fixed. It comes back because nothing was watching that behaviour.

If a bug is fixed and no test covers it, the same bug will return whenever someone refactors that area. It might take three months. It will happen.

How to fix it

Make the test part of the definition of done for a bug fix, not a follow-up task. Follow-up tasks do not get done.

For each fixed bug, pick one:

  • An automated test that fails on the old code and passes on the new code.
  • A line added to the regression checklist for that module, with the exact steps and expected value.
  • A note explaining why neither is possible — for example, a one-off data problem that cannot recur.

The third option is legitimate. Forcing a test onto every bug produces slow, useless test suites. What matters is that someone made the call on purpose.

Cause 4: environment mismatch

The fix works. It just is not where the tester is looking.

Common versions of this:

  • The fix is merged but not deployed to the environment the tester uses.
  • The tester's browser has an old bundle cached.
  • Staging points at a different API version or a different feature flag setting.
  • The tester's account has different permissions or different data than the developer's.

None of these are real defects, but they all produce reopens, and each one costs a full round trip of comments.

How to fix it

Record the environment on both sides. The bug report needs the environment where the bug was seen. The fix comment needs the environment and build where the fix was verified.

A workable minimum for both:

  • URL, for example https://staging.example.com
  • Build or commit, for example 2026.7.3-rc2 or a91f3c7
  • Browser and version, for example Chrome 138 on macOS 15
  • Account used, for example [email protected]
  • Any feature flags that are on

Collecting all of that by hand is tedious, and tedious steps get skipped. Tools like Crosscheck capture the URL, browser, console logs, and network requests automatically when you report a bug from the page, which removes the most common excuse for a missing environment section.

Then add one rule to verification: the tester states the build number they tested. "Still broken" is not a verification result. "Still broken on 2026.7.3-rc2, staging, Chrome 138" is.

A verification handoff that holds

Use these steps when a developer moves a bug to Ready for Test.

  1. The developer writes the cause in one sentence.
  2. The developer lists the files or modules changed.
  3. The developer names the build and environment where the fix is live.
  4. The developer confirms the "Fixed means" line still matches, or edits it and says why.
  5. The tester runs the original steps on the named build.
  6. The tester runs the two nearest related paths, then closes or reopens with a build number.

Step 6 is what catches partial fixes before the customer does.

Measuring the reopen rate without blame

The formula is simple:

Reopen rate = (bugs reopened in the period / bugs closed in the period) x 100

If 12 of 150 closed bugs were reopened in a sprint, the rate is 8 percent.

Treat this as a process metric, never a personal one. The moment a reopen rate appears on an individual's review, two things happen: developers argue every reopen, and testers stop reopening borderline cases. The number goes down and the product gets worse.

Instead, tag every reopen with one of the four causes above and review the split monthly. The split tells you what to change:

Dominant causeWhat to change
Vague acceptanceAdd a required "Fixed means" field to the bug template
Partial fixesRequire the cause and changed files in the fix comment
Missing regression caseMake a test or checklist line part of done
Environment mismatchRequire build number on both report and verification

Five reopens all caused by environment mismatch is a ten-minute fix to your template. Five reopens with five different causes is a different conversation. You cannot tell those apart from the count alone.

What a good rate looks like

Do not chase zero. A team with a zero percent reopen rate is usually a team where verification is a formality.

As a rough guide, 5 to 10 percent is healthy for most product teams. Under 5 percent, check that anyone is actually verifying fixes. Over 15 percent, work through the four causes in order — acceptance clarity almost always gives the biggest drop for the smallest effort.

Measure the trend over a quarter, not week to week. One bad sprint with a tricky module is not a signal.

Frequently asked questions

What is a good reopen rate for defects?

Most teams land between 5 and 10 percent of closed bugs. Rates over 15 percent usually point at unclear acceptance criteria or missing environment details rather than weak developers.

Who should be allowed to reopen a bug?

Anyone who can reproduce it on a named build. Restricting reopens to a single role slows the loop and pushes people to file near-duplicate tickets instead.

Should a reopened bug keep its original ticket number?

Yes, if the original problem still happens. Keeping one number preserves the whole history. File a new ticket only when the fix worked and a different problem appeared.

How do I stop arguments over whether a fix worked?

Write the pass condition with real values before the work starts. When the ticket says the total must read 80.00 USD, there is nothing left to debate.

Does a high reopen rate mean developers are careless?

Usually not. It more often means the handoff is thin: no cause written down, no build number, no clear pass condition. Fix the handoff before looking at people.

Related Articles

Contact us
to find out how this model can streamline your business!

Trusted by thousands ofengineering teams worldwide.

Add to Chrome
200+ reviews · 100k+ users
Crosscheck browser extension capture controls

Join the Crosscheck Community

Stay in the loop with Crosscheck's newest features and insights.