Walk through the states a tracked product defect passes through, and who moves it between them?
answer
- A small state machine, not a list
- Every move has a named owner
- Side exits matter as much as the path
- Fixed and verified are different states
- Reopen returns it to assigned, not new
basics
~20 sA tracked defect moves through new, triaged, assigned, fixed, verified and closed, with side exits to rejected, deferred and reopened. The reporter files it, triage accepts and assigns it, a developer fixes it, and someone other than the fixer verifies it before close.
solid answer
~50 sA defect lifecycle is a small state machine where **every transition has an owner**. The reporter files it as *new*. A triage group reads it, decides it is a real defect and moves it to *triaged/accepted* with a route — or rejects it with a reason. Once an engineer owns it, it is *assigned*; when the change is in a build, the engineer moves it to *fixed*. Only then does confirmation happen: **someone other than the fixer** replays the reported steps on a build that carries the fix and moves it to *verified*. Close is the administrative end, usually at release. The side exits matter as much as the happy path: *rejected* (with one of a small set of reasons), *deferred* onto a known-issue list, and *reopened* when a verification or a later build shows the failure again. The invariants are one open state at a time, a named owner for every move, and a recorded reason for every terminal state.
code
pseudocode · 19 linestransitions = {
NEW: { TRIAGED: "triage_group", REJECTED: "triage_group", DEFERRED: "triage_group" },
TRIAGED: { ASSIGNED: "team_lead", DEFERRED: "triage_group" },
ASSIGNED: { FIXED: "assignee", DEFERRED: "triage_group" },
FIXED: { VERIFIED: "verifier", REOPENED: "verifier" },
VERIFIED: { CLOSED: "release_owner", REOPENED: "anyone" },
DEFERRED: { TRIAGED: "triage_group" },
REOPENED: { ASSIGNED: "team_lead" }
}
function move(defect, to, actor, reason):
allowed = transitions[defect.state]
require(to in allowed, "illegal transition")
require(role_of(actor) == allowed[to] or allowed[to] == "anyone")
require(to != VERIFIED or actor != defect.fixed_by, "fixer cannot verify")
require(reason is not empty)
defect.state = to
defect.history.append(actor, to, reason, now())
notify(defect.reporter) if to in TERMINAL_STATESgo deeper
Be ready to name the states in order and say who moves each one. The two facts most often checked are that fixed and verified are different, and that the person who fixed it does not verify it.
An interviewer expects you to explain the side exits — rejected, deferred, reopened — and what fixed must mean for a verifier to act on it. Be able to say why reopen returns to assigned rather than to a new report.
Show the judgment: what you do about defects ageing in one state, how you keep fixed honest when builds are infrequent, and how you keep the reporter in the loop on terminal transitions so reporting does not dry up.
Own the design of the machine itself. Be ready to argue how few states a team really needs, which transitions must be role-restricted, and how the lifecycle stays the same across teams with different tooling so the process is comparable.
## Why a lifecycle exists at all A defect report is a request for someone to make a decision. Left as free text it drifts: nobody knows whether it has been read, whether anyone agreed it is a defect, whether the fix is in a build a tester can reach, or whether it is safe to stop caring about it. The lifecycle is the smallest machine that answers those questions at a glance, and its real product is **ownership** — at any moment exactly one role is expected to act. ## The states - **New / open.** Filed, not yet read by anyone empowered to route it. The only expectation is that it will be looked at inside an agreed window. - **Triaged / accepted.** A triage group has read the report, agreed it describes a real defect in the product, and given it a route. This is the state where the report stops being one person's opinion and becomes work the team acknowledges. - **Assigned.** A named engineer owns it. An accepted defect with no assignee is a backlog item, not work in flight; teams that skip this distinction lose track of what is actually being worked. - **Fixed / resolved.** The engineer believes the change is complete **and is in a build somebody else can run**. That second half is the part teams get wrong: "fixed on my branch" is not fixed, because nothing downstream can act on it. - **Verified.** A person other than the fixer has replayed the reported steps on that identified build and seen correct behaviour. This is the only state that carries independent evidence. - **Closed.** Administratively finished, usually tied to a release. Some teams close at verification and treat the release link as metadata; either convention is fine as long as it is written down. Side exits: - **Rejected** — with an explicit reason from a small fixed set, never a bare close. - **Deferred** — accepted as a real defect but not fixed for this release; it belongs on a known-issue list with an owner and a revisit point. - **Reopened** — verification failed, or the failure returned in a later build. - **Duplicate** — a form of rejection that links the report to the canonical one instead of discarding it. ## Who moves what The exact role names differ by team, but the pattern is stable: | Move | Owner | | --- | --- | | → new | the reporter (tester, developer, support, a monitoring alert) | | new → triaged / rejected / deferred | the triage group — typically product, a development lead and a test lead together | | triaged → assigned | the triage group or the owning team's lead | | assigned → fixed | the engineer doing the work | | fixed → verified / reopened | a verifier who is **not** the fixer | | verified → closed | the release or test owner | The one rule worth defending in an interview is the last transition before close. A developer who both fixes and verifies is checking their own mental model twice; the failure mode is not dishonesty, it is that they reproduce the bug the way they *understood* it rather than the way it was *reported*. ## Fixed is not verified — a worked example A tax-filing wizard drops the final page of a return when the submission crosses local midnight. An engineer traces it to a clock-skew artefact between two components, patches the comparison, and moves the defect to fixed. If the same engineer verifies it, they will re-run their own reduced case — the one that made the skew visible — and it will pass. An independent verifier instead replays the *reported* steps on the identified build, and discovers the wizard now keeps the page but stamps it with the wrong filing date. Same code change, different verdict, purely because the oracle was the original report rather than the fixer's model of it. ## What reopen means A returning failure goes back to **assigned**, not to new. Re-filing loses the history: the original evidence, the earlier triage decision, and the fact that a fix was already attempted and did not hold. Whether reopening resets the state to assigned or to triaged is a team convention — what is not optional is that the same record carries the second round. ## Invariants worth stating out loud 1. One open state at a time; a defect is never simultaneously fixed and deferred. 2. Every transition records who moved it and why — a state change with no reason is unreviewable later. 3. Every terminal state carries a resolution: fixed-and-verified, one of the rejection reasons, or deferred. 4. The reporter is notified on terminal transitions and has a right of reply; a silent rejection is how teams train people to stop reporting. 5. A defect that has sat in one state past an agreed age is a triage input, not a fact of nature. An interviewer asking this is checking whether you have lived inside a real process or only read the state names. The states are easy; naming the owner of each move, and defending why the fixer does not verify, is what separates the two.
- Why should a returning failure reopen the original defect instead of being filed as a new report?The original record holds the reproduction steps, the evidence, the triage decision and the fact that a fix was already attempted and did not hold. A fresh report throws all of that away and hides that this is round two, which is exactly the signal the team needs. Reopening keeps one history per failure and puts the defect back in the assigned lane rather than at the back of the untriaged queue.
- What does a triage decision to batch several related defects actually change about their lifecycle?Batching assigns a group of defects to one owner and one change so they are fixed and confirmed together — typically defects sharing a root cause or a screen. Each defect keeps its own record and its own verification, because closing four reports on one person's say-so hides the ones the change did not actually touch. The batch is a scheduling device, not a merge.
- A defect has sat in the fixed state for three weeks because no build has been produced. What is wrong there?Fixed is supposed to mean the change is in a build a verifier can run; if no such build exists, the state is lying and the defect is invisible to everyone waiting on it. Either the team defines fixed as merged and adds a separate awaiting-build state, or that defect belongs back in assigned. Ageing in a single state is itself a triage trigger.
It is a relay, not a solo run: the baton has to be visibly in one runner's hand at a time, and the last handover is deliberately to a different person.
saying these in an interview costs you the question
- Says the developer closes their own defect after fixing it
- Treats fixed and verified as the same state
- Rejects a report with no recorded reason
- Cannot name which role owns each transition
- Files a new report instead of reopening the original
- Thinks deferred means the defect quietly disappears