The Defect Life Cycle — From Discovery to Closure

Finding a defect is only the beginning. What happens after discovery — how the defect is reported, assigned, fixed, verified and closed — determines whether the bug actually gets resolved or disappears into a backlog graveyard. The defect life cycle is the structured workflow that every bug follows from the moment a tester discovers it to the moment it is confirmed fixed and closed. Understanding this cycle is essential because your role as a QA engineer does not end when you file the bug — it ends when you verify the fix and close the ticket.

Defect Life Cycle — Status Transitions and Responsibilities

Every defect tracking system uses a set of statuses that represent where a bug is in its journey. While exact status names vary between tools, the underlying flow is consistent across the industry.

# Modelling the defect life cycle with status transitions
DEFECT_LIFECYCLE = {
    "New": {
        "description": "Tester has discovered and reported the defect",
        "owner": "QA Engineer",
        "next_states": ["Open", "Rejected"],
        "action": "Triage team reviews the defect report",
    },
    "Open": {
        "description": "Defect accepted and assigned to a developer",
        "owner": "Development Lead",
        "next_states": ["In Progress", "Deferred"],
        "action": "Developer is assigned and begins investigation",
    },
    "In Progress": {
        "description": "Developer is actively working on the fix",
        "owner": "Developer",
        "next_states": ["Fixed"],
        "action": "Developer implements and unit-tests the fix",
    },
    "Fixed": {
        "description": "Developer has committed a fix; ready for QA verification",
        "owner": "Developer",
        "next_states": ["Verified", "Reopened"],
        "action": "QA engineer re-tests the scenario from the original report",
    },
    "Verified": {
        "description": "QA confirms the fix works; defect is resolved",
        "owner": "QA Engineer",
        "next_states": ["Closed"],
        "action": "QA marks the defect as verified after successful re-test",
    },
    "Closed": {
        "description": "Defect is fully resolved and archived",
        "owner": "QA Engineer / Manager",
        "next_states": [],
        "action": "No further action required",
    },
    "Reopened": {
        "description": "QA re-test failed — the fix did not work or caused a regression",
        "owner": "QA Engineer",
        "next_states": ["In Progress"],
        "action": "Sent back to developer with updated reproduction steps",
    },
    "Rejected": {
        "description": "Triage team determined it is not a valid defect",
        "owner": "Triage Team",
        "next_states": ["Reopened", "Closed"],
        "action": "Reason documented: duplicate, works as designed, not reproducible",
    },
    "Deferred": {
        "description": "Valid defect but will not be fixed in this release",
        "owner": "Product Owner",
        "next_states": ["Open"],
        "action": "Scheduled for a future sprint with documented justification",
    },
}

print("Defect Life Cycle — Status Transitions")
print("=" * 60)
for status, info in DEFECT_LIFECYCLE.items():
    transitions = " → ".join(info["next_states"]) if info["next_states"] else "(terminal)"
    print(f"\n  [{status}]")
    print(f"    {info['description']}")
    print(f"    Owner: {info['owner']}")
    print(f"    Transitions to: {transitions}")
Note: The “Reopened” status is critical and often misunderstood. When a QA engineer re-tests a fix and it fails, the defect must be reopened — not closed and re-filed as a new bug. Reopening preserves the full history of the defect: the original report, the attempted fix, the failed verification, and the subsequent successful fix. This history is invaluable for root cause analysis and for identifying developers or modules that frequently produce incomplete fixes.
Tip: When verifying a fix, do not only re-run the exact steps from the original bug report. Also test related scenarios to check for regressions. A developer who fixed a null pointer exception in the login form might have accidentally broken the “remember me” checkbox. Expanding your re-test scope catches these side effects before they reach production.
Warning: Never close a defect without verifying the fix yourself on the actual test environment. “Developer says it is fixed” is not verification. Developers test in their local environment, which may differ from staging or production. Your job is to confirm the fix works in the environment where it matters — the one that mirrors what real users will experience.

Common Mistakes

Mistake 1 — Closing defects without verification

❌ Wrong: Developer marks a bug as “Fixed” and the QA engineer closes it immediately without re-testing.

✅ Correct: QA engineer deploys the new build to the test environment, re-executes the original reproduction steps, confirms the defect is resolved, checks for regressions, and only then moves the status to “Verified” and subsequently “Closed.”

Mistake 2 — Filing duplicate defects instead of checking existing reports

❌ Wrong: Finding a bug and immediately filing a new report without searching the defect tracker for existing reports of the same issue.

✅ Correct: Searching the defect tracker by keywords, module and error message before filing. If a duplicate exists, add your reproduction details as a comment on the existing ticket instead of creating a new one. Duplicates waste triage time and fragment the defect history.

🧠 Test Yourself

A developer marks a defect as “Fixed.” What should the QA engineer do next?