Why 'Testing Gaps' Are Usually Requirements Gaps
Share this post

The postmortem had been running for eleven minutes before anyone asked the question that mattered. Up to that point, the conversation had followed its usual shape: a timeline of when the defect was introduced, when it was detected, when it was fixed, and how long each step took. Then the VP of Engineering asked it, not with hostility, just with the tired curiosity of someone who has asked it a dozen times before: "How did QA not catch this?"

The ticket that had shipped the defect was, on its face, unremarkable. It read: "As a support agent, I want to issue a partial refund to a customer so that I can resolve billing disputes without needing engineering help." Three acceptance criteria followed. A support agent can select an order and enter a refund amount. The refund amount cannot exceed the original order total. The customer receives a confirmation email.

The QA engineer who tested it did exactly what those three criteria asked. She confirmed a support agent could select an order, enter an amount, and that the system rejected any amount greater than the order total. She confirmed the email arrived. She marked the ticket passed, and it shipped.

Nine weeks later, a customer who had already received one partial refund on a disputed order called in a second dispute on the same order. A different support agent, working from the same interface, issued a second partial refund. The two refunds together exceeded the order total by forty percent, because nothing in the system — and nothing in the ticket — accounted for the running total of refunds already issued against that order. The finance team caught it during reconciliation three weeks after that, by which point eleven more orders showed the same pattern.

Nobody in that postmortem was lying when they asked how QA missed it. They were asking a sincere question rooted in a common but mistaken assumption: that a tester's job is to imagine every way a system could misbehave and then confirm it doesn't. That is not what a test does. A test checks a system against a stated expectation. The three acceptance criteria on that ticket described a single refund against a single order. They said nothing about cumulative refunds, nothing about a second refund attempt on an already-adjusted order, nothing about the state that mattered. The QA engineer did not fail to test a known scenario. She tested every scenario the ticket described, correctly, and the ticket described the wrong scope of the problem.

This is not an isolated failure of one ticket-writer having a bad week. It is a pattern that repeats across teams that otherwise have competent QA, competent engineers, and reasonable release processes, because the pattern does not originate in testing. It originates one step earlier, in the requirement.

The Question Postmortems Ask, and Why It Usually Misdirects the Fix

"Why didn't QA catch this?" is a question about execution. It presumes that the correct behavior was knowable in advance, that it was written down or otherwise available to the person testing, and that the gap between what shipped and what should have shipped is a gap in verification effort, tooling, or attention. Sometimes that presumption is accurate. A tester skips a regression suite under deadline pressure and a known defect resurfaces. A flaky end-to-end test gets quarantined and never triaged, and the scenario it covered breaks silently. An engineer merges a hotfix outside the normal review path and nobody re-runs the affected test cases. These are genuine testing failures, and treating them as such — with better test discipline, better CI gating, better test maintenance — is the right response.

But a defect can also reach production because the specification the team built against never described the behavior that turned out to matter. In that case, asking "why didn't QA catch this" sends the organization looking for the fix in the wrong department. It usually produces one of three unhelpful outcomes.

The first is a demand for more testing, applied indiscriminately. Leadership asks for more regression coverage, more exploratory testing hours, or a slower release cadence with more manual sign-off. This adds cost without addressing the actual mechanism, because the missing scenario was never a known test case that got skipped — it was a scenario nobody, including the most careful tester in the world, had reason to construct from the information available.

The second is reputational damage to QA that is not deserved and does not improve anything. A tester or QA team develops a defensive posture, starts writing exhaustive test plans for every conceivable edge case regardless of whether the product actually needs that behavior defined, and slows delivery without closing the actual gap, because the exhaustive test plan is still an act of guessing at intent rather than verifying an agreed specification.

The third, and the most corrosive over time, is that the actual fix — improving how acceptance criteria get written and reviewed before development starts — never happens, because nobody in the postmortem is looking there. The defect gets classified under "QA," the root-cause field in the tracker gets set to "testing gap" or left blank, and the same category of defect recurs in the next quarter with a different feature attached to it.

None of this means testing is blameless by default or that QA teams never miss things they should have caught. It means the diagnostic question in a postmortem needs to be more precise than "why didn't QA catch this," because that phrasing assumes the answer lives in one department before anyone has checked.

A more useful diagnostic question is: was the behavior that broke ever specified anywhere a tester could have found it, in language precise enough that two competent people would test it the same way? If the answer is yes and it still slipped through, that is a testing failure worth investigating on its own terms. If the answer is no, the postmortem needs to move upstream, and continuing to interrogate the testing process will not find the actual defect in the system — which is the process that produced the ticket in the first place.

A Taxonomy of Root Causes: Separating Testing Failures From Requirements Failures

Not every escaped defect belongs in the same bucket, and lumping them together is exactly what keeps organizations from fixing the right thing. The table below separates the common root-cause categories behind production defects that get informally filed under "QA missed it." The distinction that matters most is the middle column: was there ever a stated, testable expectation for a competent tester to check the system against?

Root cause category Was correct behavior specified anywhere? Typical signature Who owns the fix
Genuine testing failure — a known, specified scenario existed and was skipped, mistested, or regressed Yes, explicitly A test case existed or should obviously have existed from the stated criteria; it wasn't run, was run incorrectly, or a later change silently broke it QA / engineering test discipline
Missing edge case in the spec — the ticket described the happy path only; a boundary condition (empty state, maximum value, expired session, zero-quantity) was never mentioned No The bug involves a boundary or unusual-but-foreseeable input that nobody wrote down Product, with engineering/QA input during refinement
Ambiguous acceptance criteria — the wording allows two reasonable, mutually exclusive interpretations Partially — something was written, but it doesn't resolve to one behavior Two testers, or the same tester on two different days, would reasonably test it differently; a "which did you mean" conversation happens after the fact, not before Whoever wrote the criteria, caught during refinement review
Implicit assumption never written down — the author assumed shared context ("obviously refunds can't exceed what's left to refund") that wasn't obvious to anyone building or testing the feature No The post-incident explanation includes the phrase "well, obviously it should..." Product and engineering jointly; a refinement process gap
Permission or role scenario nobody considered — the ticket describes behavior for one role and is silent on others who can reach the same interface or API No, for the untested role The defect involves a role, tenant, or account type outside the one the ticket named Product, informed by who actually has access to the feature
Concurrent or multi-actor scenario absent — the ticket implicitly assumes single-user, single-request behavior No The defect only appears when two actions overlap in time (two refunds, two edits, a race between a webhook and a UI action) Product and engineering jointly; often needs an architecture conversation, not just a criteria fix
Environment or data mismatch — the behavior was correctly specified and correctly tested, but the test environment didn't represent production data shape, scale, or configuration Yes Passes in QA, fails in production against real data volume, real third-party responses, or real account configurations Engineering/QA environment parity, not requirements

The categories in the middle of this table — missing edge case, ambiguous criteria, unwritten assumption, uncovered role, absent concurrency scenario — share a common signature: a competent tester working only from the ticket as written would not have produced a test for the scenario that broke, because nothing pointed them toward it. That is different from a tester overlooking something the ticket did describe. Confusing the two is what keeps postmortems unproductive.

It is worth being honest about where this taxonomy gets fuzzy. "Obviously implied" behavior is a matter of degree — a system that lets users enter negative numbers into a quantity field and doesn't reject them is arguably a case where basic input validation should be assumed regardless of what the ticket says, and a QA engineer who doesn't think to try a negative number is missing a baseline check, not waiting on product to spell it out. The line moves depending on how domain-specific the missing behavior is. Basic input hygiene, injection safety, and standard error handling belong to engineering and QA judgment by default. Business logic decisions — what happens on a second refund, what a suspended account can still see, whether a report includes soft-deleted records — belong to product, because they encode a business decision that testing cannot infer from good practice alone.

In short: a testing gap is a scenario that was specified and not verified correctly; a requirements gap is a scenario that was never specified at all, so there was nothing precise for verification to check against. The two require different fixes — better testing discipline for the first, better upstream specification and review for the second — and a postmortem that doesn't separate them will keep prescribing the first fix for a problem that is actually the second one.

Diagnostic Questions for the Postmortem

Before assigning a root-cause tag to a defect, run it through a small set of diagnostic questions. These are designed to be answerable in five minutes without turning the postmortem into a tribunal.

  1. Was the behavior that broke described, anywhere, before the code was written — in the ticket, in a linked design doc, in a refinement meeting note? Not "would a thoughtful person have anticipated it," but was it actually written down somewhere a tester could have found it.
  2. Would two competent testers, working only from what was written, have built the same test case? If the honest answer is "it depends how you read criterion two," the defect is an ambiguity failure, not an execution failure.
  3. Does the fix change what the acceptance criteria say, or does it change what the test suite checks against unchanged criteria? If the fix requires rewriting or adding to the acceptance criteria, the root cause sits upstream of testing.
  4. If this exact ticket, worded exactly as it was, were handed to the QA team again today, would they test it any differently? If not, more QA effort would not have prevented the recurrence; only a different ticket would have.
  5. Was the missing behavior a business decision (what should happen) or an execution defect (the agreed behavior wasn't built or verified correctly)? Business decisions belong in acceptance criteria before development starts. Execution defects belong to engineering and QA process.
  6. Did the environment or data used in testing represent the conditions under which the defect occurred? If the specified behavior was correct and was tested correctly but the environment didn't match production, the fix is environment parity, not requirements clarity.

These questions are deliberately structured so that the default outcome is not "blame product" any more than it is "blame QA." A team that runs every escaped defect through this list for a quarter will typically find that the root-cause distribution is genuinely mixed — some real testing gaps, some environment gaps, and a substantial share of requirements gaps. The value is in getting an accurate distribution instead of defaulting every incident to whichever department is easiest to point at in the moment, which in most organizations is QA, because QA is the last function to touch the change before release and the most visible name attached to "approved."

What a Testable Acceptance Criterion Actually Looks Like

The difference between a requirement that supports good testing and one that doesn't is rarely about length. Vague criteria are sometimes long and specific-sounding without being testable, and a short criterion can be precise enough to test cleanly. The difference is whether the criterion resolves to a single, checkable behavior or leaves room for interpretation.

Bill Wake's INVEST framework, first published in 2003 and widely adopted since Mike Cohn incorporated it into User Stories Applied in 2004, names testability as one of six qualities a good user story should have — the idea being that a story carries "an implicit promise" that whoever wrote it understands what they want well enough that a test could, in principle, be written for it, even before that test exists. That promise is exactly what breaks down in the categories described above. A ticket that says "support agents can issue refunds" is negotiable, valuable, and estimable. It is not, on its own, testable, because "issue refunds" does not resolve to one behavior once more than one refund, more than one role, or more than one point in time enters the picture.

The table below shows the same category of requirement, written vaguely and then rewritten as a testable criterion, across several realistic situations.

Situation Vague acceptance criterion Testable acceptance criterion
Refund handling "Support agents can issue a refund up to the order total." "Support agents can issue one or more partial refunds against an order, provided the sum of all refunds already issued plus the new refund does not exceed the original order total. The system displays the remaining refundable balance before the agent confirms. Attempting to exceed the remaining balance returns a blocking error naming the remaining amount."
Role-based access "Admins can view account billing history." "Users with the Admin role can view billing history for accounts within their own organization. Users with the Support role can view billing history read-only, with payment method numbers masked. Users with the Viewer role cannot access the billing history endpoint or UI; both the API and the UI return a 403 and a generic access-denied message, not a 404, so the account's existence is not disclosed."
Session and concurrency "Users can edit their profile." "A user can edit their profile from a single active session. If the same account is edited from two sessions concurrently, the second save is rejected with a conflict message showing what changed, rather than silently overwriting the first save. Session expiry during an edit preserves unsaved form data locally and prompts re-authentication rather than discarding input."
Pagination and empty states "The dashboard shows a list of invoices." "The invoice list loads 25 rows per page with pagination controls. When an account has zero invoices, the list shows an explicit empty-state message, not a blank table. When an account has exactly one page of results, pagination controls are hidden. Sort order defaults to invoice date descending and is stated explicitly, not left to database insertion order."
Third-party integration failure "Orders sync to the accounting system." "When the accounting system's API is unreachable or returns an error, the order is queued for retry with exponential backoff for up to 24 hours, and the order's internal status remains 'pending sync' rather than 'synced.' After 24 hours of failed retries, the order is flagged for manual review and an alert is sent to the finance ops channel."

The right-hand column is longer, but the added length is not padding. Each sentence closes off a specific interpretation that the vague version left open, and each closed interpretation is a test case a QA engineer can write directly from the ticket, without guessing what the author meant or scheduling a meeting to ask. That is the practical test of whether a criterion is testable: can someone who was not in the room when it was written build the correct test case from the text alone? If the answer requires asking the author, the criterion has not yet done its job, however detailed it looks.

It is also worth naming what over-correction looks like, because teams that internalize this lesson sometimes swing too far. A testable criterion is not the same as an exhaustive one. The refund example above does not need to enumerate every currency, every tax jurisdiction, and every possible payment method failure mode within the acceptance criteria themselves — those belong in a linked technical design note or a shared team convention, not repeated on every ticket that touches money. The goal is to close the interpretation gaps that would otherwise produce different behavior depending on who built or tested the feature, not to write a specification so dense that refinement takes longer than development.

Three Scenarios Where the Ticket, Not the Tester, Was the Problem

The following three scenarios are hypothetical composites built to illustrate common patterns. They do not describe any real QAtronic client, engagement, or outcome.

Hypothetical scenario: the permission boundary nobody named

A B2B SaaS company building a project management tool shipped a feature allowing "Project Owners" to archive a project. The ticket read: "As a Project Owner, I want to archive a completed project so that it no longer appears in the active project list." Acceptance criteria specified that archiving moves the project out of the active list, that archived projects appear in a separate archive view, and that archived projects can be restored by the same Project Owner.

The hidden assumption was about who else could reach an archived project. The ticket said nothing about Project Members — users with edit access but not ownership — and nothing about what happens to in-progress work assigned to a member when a project they're actively using gets archived out from under them. In production, a Project Member with three open tasks in a project lost access to the task board mid-edit when the owner archived the project, with no warning and no clear path back to their unsaved work. The member filed a support ticket describing data loss; nothing had actually been deleted, but the member could not find their own tasks and reasonably concluded they were gone.

QA had tested archiving exactly as described: as the Project Owner, confirming the project left the active list and could be restored. Nothing in the ticket asked what a Project Member should see or be warned about. The technical cause was straightforward — the archive action didn't check for other active sessions on the project or notify affected members — but the organizational cause was that "who else has access to this and what should they experience" was never asked during refinement, because the ticket was written entirely from the owner's point of view.

A testable version of the acceptance criteria would have added: "When a project with active Project Members is archived, each member with an open session on that project receives a non-blocking notification that the project has been archived, with a link to the archive view. Unsaved form state in an open task at the moment of archiving is preserved locally and is not lost." Writing that sentence during refinement requires someone in the room to ask, out loud, "who else can be using this feature at the same time as the person this ticket is written for?" That is a product and design question as much as an engineering one, and it is exactly the kind of question a role-scenario check catches before the ticket enters a sprint.

Hypothetical scenario: the happy-path ticket in a fintech onboarding flow

A fintech company building a small-business lending product shipped a KYC (know-your-customer) document upload step. The ticket described the primary flow: a user uploads a government ID and a proof-of-address document, the system runs them through a third-party verification API, and on success the user proceeds to the next onboarding step. Acceptance criteria covered file type validation, file size limits, and the success path through the verification API.

Nothing in the ticket addressed what happens when the verification API times out, returns an ambiguous result, or when the same user uploads a second, different ID after already passing verification once — a scenario that turned out to matter because the product allowed users to go back a step during onboarding. In production, a user who timed out on their first verification attempt, retried, and succeeded ended up with two verification records in the backing system, one "failed" and one "passed." A downstream compliance report that pulled the most recent record by timestamp occasionally pulled the failed one due to a race in how the two API callbacks landed, and flagged an approved, verified customer as unverified inside the compliance dashboard — a false negative that, if acted on automatically in a later release, could have frozen a legitimate customer's account.

This was not a case QA skipped through carelessness. The ticket's three acceptance criteria were tested faithfully. Retry behavior, duplicate submission handling, and what "verified" means when more than one verification record exists for the same user were never specified, because the ticket was written entirely around the single successful pass through the flow — a "happy path only" ticket in the taxonomy above. The fix that shipped afterward added an explicit criterion: "Verification status is derived from the most recent terminal result (passed or definitively failed), not the most recent timestamp; a timed-out or errored attempt does not overwrite a prior passed result, and does not count as terminal." That sentence encodes a business decision about which record should win, a decision that engineering should not have had to infer on its own and that QA could not have tested for without it being made explicit somewhere first.

Hypothetical scenario: the ambiguous word "current" in an e-commerce inventory ticket

An e-commerce marketplace shipped a feature letting sellers set a "low stock" threshold that triggers an automatic reorder alert. The ticket read: "As a seller, I want to be alerted when my current inventory drops below my configured threshold." The acceptance criteria stated that an alert fires when inventory drops below the threshold and that the alert appears in the seller's notification center.

Two engineers building adjacent parts of the system read "current inventory" differently. One took it to mean the inventory count net of items already in open, unfulfilled orders — reflecting what was actually still sellable. The other took it to mean the raw warehouse count, unadjusted for pending orders. Both readings are reasonable; the ticket does not resolve which one is correct, and neither engineer flagged the ambiguity because each was confident their own reading was the obvious one. The alert logic and the seller-facing inventory display ended up using different definitions of "current," built by two people who never compared notes because nothing in the ticket forced the question into the open.

The result: sellers with a healthy raw warehouse count but a large number of pending unfulfilled orders sometimes saw no low-stock alert at all, even though their sellable inventory — the number that actually mattered for whether they could fulfill the next order — had effectively hit zero. A seller who ran out of sellable stock without warning missed several orders before the discrepancy was traced back to the two conflicting definitions.

A testable version would have named the exact quantity from the start: "Inventory, for the purpose of the low-stock alert, means the warehouse count minus the sum of quantities reserved for unfulfilled open orders. This is the same quantity displayed to the seller as 'available to sell' elsewhere in the dashboard." Once that sentence exists, there is nothing left to interpret, and a QA engineer can write a test that reserves inventory against an open order and confirms the alert threshold reacts to the adjusted number, not the raw one. The bug here was never a coverage gap. It was a word — "current" — carrying two defensible meanings that nobody forced apart before two different engineers built against two different mental models of the same word.

Hypothetical scenario: rescheduling that silently dropped an insurance check

A healthcare scheduling platform used by outpatient clinics shipped a self-service appointment rescheduling feature so patients could move an existing appointment to a new date without calling the front desk. The original booking flow, built a year earlier, ran an insurance eligibility check against the payer's system whenever a new appointment was created, and the check was treated as such a basic, load-bearing part of booking that nobody thought to mention it again when the rescheduling ticket was written. That ticket read: "As a patient, I want to move my appointment to a different available date and time so that I don't have to call the clinic." Acceptance criteria covered date and time selection, availability conflicts with the provider's calendar, and a confirmation notification.

The unwritten assumption was that rescheduling was really just "the same appointment, different timestamp," and that whatever insurance verification had already happened at booking time was still valid. It usually was — except when a patient rescheduled far enough into the future that their coverage had changed, or moved an appointment across a plan year boundary, or switched employers between the original booking and the new date. None of those conditions were rare enough to ignore; over a few months, a noticeable share of rescheduled appointments were going ahead without a current eligibility check, and the clinic was finding out the coverage had lapsed only when the claim was filed and denied, well after the visit.

QA had tested rescheduling thoroughly against the ticket as written: date selection, calendar conflicts, confirmation delivery, and cancellation of the original slot. Nothing in the ticket said eligibility needed to be re-verified, because nobody involved in writing it was thinking about insurance at all — that logic lived in a different part of the codebase, owned by a different engineer, built for a different ticket a year prior, and the rescheduling ticket's author reasonably assumed the two flows shared more machinery than they did. This is the implicit-assumption category from the taxonomy above in a particularly costly form, because the assumption wasn't even wrong out of carelessness — it was wrong because the two people who understood the two halves of the system never had a conversation about how rescheduling should relate to the eligibility check that made the original booking valid in the first place.

A testable acceptance criterion would have made the dependency explicit rather than assumed: "Rescheduling an appointment to a new date triggers a fresh insurance eligibility check against that new date, using the same verification service as new bookings. If eligibility cannot be confirmed for the new date, the patient sees a message directing them to contact the clinic before the reschedule is finalized, and the original appointment remains active until eligibility is resolved." Writing that sentence requires someone to ask, during refinement, whether this new flow inherits or bypasses a safeguard built into an older one — precisely the kind of question a testing-minded participant in refinement is positioned to ask, and precisely the kind of question a product manager working from the rescheduling user story alone, without that system-level context, would have no particular reason to raise on their own.

Why This Is a Collaboration Problem, Not Just a Writing Problem

It is tempting to conclude from the examples above that the fix is simply "product managers should write more detailed tickets." That is only half true, and treating it as the whole truth creates a new failure mode: a product manager working alone, however skilled, cannot reliably anticipate the technical edge cases, role interactions, and failure modes that an engineer or QA specialist would surface immediately given the chance to ask questions before development starts. The refund example, the archive example, and the inventory example all share a structural cause beyond "the ticket was vague" — nobody with a different vantage point reviewed the ticket closely enough, before development, to notice what was missing.

This is the practical justification for the "three amigos" practice, a collaborative refinement technique described by the Agile Alliance as bringing together three perspectives — usually product or business, development, and testing — to examine a piece of work before it enters development. The Agile Alliance frames the three questions each perspective is meant to bring as distinct and complementary: business asks what problem is being solved, development asks how a solution might be built, and testing asks what could possibly happen — the question most likely to surface the edge case, the second role, the concurrent actor, or the ambiguous word that a single author, working alone, would not think to raise. The practice does not require exactly three people in the room; the Agile Alliance is explicit that it is a guide toward including the necessary perspectives, not a rigid headcount, and that additional relevant stakeholders should be included when a piece of work calls for it.

The mechanism that makes this work is not the meeting itself. It is that a tester in the room, reading a ticket before code exists, asks a structurally different question than an engineer does. An engineer reading "support agents can issue refunds" is likely to ask how to build it — what the data model looks like, which service owns the refund logic. A tester reading the same sentence is more likely to ask what happens the second time, what happens if the amount is negative, what happens if the order was already fully refunded, what happens if two agents act on the same order within seconds of each other. Both questions are necessary. Only one of them tends to surface the missing acceptance criteria before they become a production defect, which is exactly why testing perspective needs a seat during refinement, not only during verification after the ticket is already built.

Where this practice fits depends heavily on team size and structure, and it is worth being specific about that rather than prescribing one format for every organization.

Small teams (roughly five to fifteen engineers, one or two product managers). A formal three-amigos meeting as a separate calendar event is often overkill; the same effect can be achieved inside backlog refinement itself, provided the PM does not run refinement as a one-way readout of already-finished tickets. The discipline that matters at this size is simpler: no ticket enters a sprint without at least one engineer and one person thinking from a testing perspective — which may be the same engineer wearing a different hat — having read it and asked at least one "what if" question out loud. The risk at this size is not lack of process; it's that refinement happens informally in Slack threads where the record of what was asked and answered disappears, so the same ambiguity resurfaces a month later on a related ticket.

Scale-ups (roughly fifteen to eighty engineers, a dedicated product function, a QA function that may or may not be centralized). This is where a lightweight, recurring three-amigos session per feature area earns its cost, because the number of people who might touch a given part of the system has grown past what one PM can hold in their head, and the informal Slack-thread version starts losing information across teams. The output that matters is not the meeting — it's a small number of acceptance criteria additions captured directly on the ticket before it's estimated, and a habit of QA involvement at refinement time rather than only at test-writing time.

Enterprises with formal requirements processes. Here the risk inverts. The organization often already has a structured requirements process — business requirements documents, formal sign-off chains, traceability matrices — and the failure mode is not lack of specification but specification that is detailed on paper and still ambiguous in practice, because the document was written by a business analyst without engineering or QA input and reviewed by stakeholders who checked it for business accuracy, not testability. A three-amigos-style review at the point where a requirement gets translated into a sprint-sized ticket — not a replacement for the formal process, but an added checkpoint inside it — closes that gap without requiring the organization to abandon its existing governance.

The Definition of Ready as a Gate, Not a Bureaucracy

Collaborative refinement produces better criteria when it happens, but it needs an enforcement mechanism, or it degrades into a practice that happens on the tickets people remember to bring up and skips the ones that are late, urgent, or written by someone confident enough not to ask for review. That enforcement mechanism is a Definition of Ready: an explicit, team-agreed checklist that a ticket must satisfy before it can be pulled into a sprint, functioning as the mirror image of a Definition of Done. Where Definition of Done governs when work is finished, Definition of Ready governs whether work should start at all. The Agile Alliance describes its core value plainly: it exists to avoid starting work on features that lack clearly defined completion criteria, because that gap "usually translates into costly back-and-forth discussion or rework" — precisely the pattern in each hypothetical scenario above, where the true cost was not the initial ticket but the incident, the postmortem, and the retroactive rewrite of criteria that should have existed from the start. A concrete benefit worth naming directly: a real Definition of Ready gives the team explicit standing to push a poorly specified ticket back to refinement, rather than accepting it into a sprint and hoping it clarifies itself along the way.

A Definition of Ready is only useful if it is specific enough to reject a ticket, not so generic that everything passes it. "Acceptance criteria are clear" is not a gate; anyone can convince themselves their own criteria are clear. The checklist below is built around the failure patterns described earlier in this article, so that each line closes a specific category of requirements failure rather than restating a vague ideal.

Definition of Ready — a starting checklist to adapt per team

  1. Every acceptance criterion describes one behavior, not a range of acceptable behaviors — if two team members would write different test cases from the same sentence, the sentence is rewritten before the ticket is estimated.
  2. At least one negative or boundary condition is specified explicitly: what happens at zero, at the maximum, on the second attempt, on an expired or invalid state — not left to be discovered during test design.
  3. Every role or account type that can reach the feature is named, along with what each one should experience — not only the role the ticket is primarily written for.
  4. If the feature can be triggered by more than one actor or process at overlapping times, the criteria state what happens on overlap — or the ticket explicitly states that concurrent access is out of scope for this iteration and why that's acceptable.
  5. Failure and error paths for any third-party or downstream dependency are named — timeout, partial failure, and unavailability are not left implicit.
  6. A person from a testing perspective — a QA engineer, or an engineer consciously testing-minded for this review — has read the ticket before estimation and confirmed they could write test cases directly from it without needing to ask the author what they meant.
  7. The ticket is small enough to estimate with reasonable confidence — INVEST's "small" and "estimable" criteria are satisfied — because an oversized ticket tends to accumulate exactly the kind of implicit scope that produces missing edge cases.
  8. Any assumption the author is relying on that isn't written in the criteria has been said out loud in refinement and, if it matters to correctness, added to the ticket rather than left as shared understanding in the room.

None of this requires a heavyweight process. A five-person startup can run this checklist as five minutes of conversation at the point a ticket is pulled into a sprint. What matters is that the checklist is applied consistently rather than only on tickets someone happens to flag as risky, because the tickets that produce the costliest defects are rarely the ones that feel risky in advance — the refund ticket, the archive ticket, and the inventory ticket in the scenarios above all looked routine when they were written. That is precisely why they made it through refinement without anyone raising a question.

Tagging Defects by Root Cause, Not Just Severity

Most defect trackers capture severity (blocker, major, minor) and sometimes a rough category (frontend, backend, data). Far fewer capture root cause in a way that distinguishes a testing failure from a requirements failure, which means the organization has no reliable way to see, over time, where its defects are actually originating. Without that data, every postmortem re-litigates the same question from scratch, and the intuitive but often wrong answer — "QA should have caught this" — wins by default because it's the easiest story to tell in the room.

The chart below is an illustrative, hypothetical example — not real industry data, and not data from any real QAtronic engagement — showing what a root-cause breakdown can look like once a team starts tagging defects using a taxonomy similar to the one earlier in this article, over a hypothetical 90-day postmortem sample.

Chart 1 — Illustrative root-cause breakdown of a hypothetical 90-day defect postmortem log

Root cause category Number of defects (hypothetical, illustrative only)
Ambiguous acceptance criteria 14
Missing edge case in the spec 11
Genuine testing failure (known scenario skipped or regressed) 9
Implicit assumption never written down 8
Environment or data mismatch 6
Permission/role scenario not considered 5
Concurrent/multi-actor scenario absent 4

What this shows: In this illustrative sample, the four requirements-originated categories (ambiguous criteria, missing edge case, unwritten assumption, uncovered role/concurrency scenario) sum to 42 of the 57 tagged defects — roughly three-quarters — against 9 defects that were genuine testing-process failures and 6 that were environment issues unrelated to either. These figures are a constructed example meant to demonstrate the shape of the distribution a team might find once it separates these categories, not a benchmark or an average drawn from real measurement. The exact split will differ by team, product maturity, and how disciplined refinement already is; the value of the exercise is not the specific numbers but the practice of tagging defects this way at all, because most organizations currently have no data to know whether their own distribution looks anything like this or nothing like it.

Building this kind of tagging into an existing tracker doesn't require new tooling. A single custom field — "root cause: testing / requirements-ambiguity / requirements-missing / implicit-assumption / environment / other" — populated during the postmortem using the diagnostic questions from earlier in this article is enough to start generating a real, team-specific version of this chart within a quarter. The habit that makes the data trustworthy is applying the diagnostic questions consistently, including on defects that are inconvenient to classify — the ones where product genuinely would rather the tag say "testing," and the ones where engineering would rather it say "requirements."

Leading Indicators: Catching the Gap Before It Ships

Root-cause tagging is a lagging measure. It tells a team, after the fact, where a defect actually originated, which is valuable for correcting the postmortem conversation but does nothing to prevent the next one. A separate, smaller set of leading indicators can be tracked during refinement itself, before a ticket ever reaches a sprint, and these are worth measuring precisely because they surface the same underlying pattern earlier and more cheaply.

The most useful of these is not intuitive at first: the number of clarifying questions a ticket receives during refinement, and when they get asked. A ticket that sails through refinement with zero questions is not necessarily a well-written ticket — on a team that has just adopted a Definition of Ready, a suspiciously quiet refinement session is often a sign that nobody read the ticket closely enough to have a question, not that the ticket was unusually clear. The number worth watching over time is not questions-at-refinement in isolation, but the ratio between questions asked during refinement and clarifying questions asked mid-sprint, after development has already started. A healthy trend moves refinement-stage questions up and mid-sprint clarification down, because a question raised during refinement costs a conversation, while the same question raised mid-sprint costs a conversation plus a context switch plus, frequently, rework on code already written against the wrong assumption.

A second useful indicator is the rate at which tickets get sent back from sprint planning to refinement for failing the Definition of Ready checklist. Zero send-backs, sustained over several sprints, is not a sign of excellent upstream writing in most organizations — it's usually a sign the checklist isn't being applied with any real teeth, because even disciplined teams should expect some fraction of tickets to need another pass. A team introducing a Definition of Ready for the first time should expect this rate to start meaningfully above zero and decline over subsequent quarters as writing habits improve, not start at zero and stay there.

A third, more advanced indicator worth tracking once root-cause tagging has a few months of data behind it is the recurrence rate of a specific requirements-failure subtype — for example, how often "permission/role scenario not considered" reappears as a tag across unrelated tickets. A single instance is a ticket-writing miss. A repeated pattern across a quarter is a signal that the team's refinement process has a structural blind spot around a specific category — commonly permissions, commonly concurrency, commonly third-party failure handling — that a one-line addition to the Definition of Ready checklist, tailored to that recurring gap, can close more efficiently than reminding people generically to "be more thorough."

None of these indicators are meant to become a scorecard used to evaluate individual product managers. Used that way, they create an incentive to pad tickets defensively or to avoid raising ambiguity in refinement for fear of being the one who "caused" a send-back, which recreates the exact silence that let the gap through in the first place. Used as a team-level signal reviewed periodically — is refinement catching more or fewer of these before the sprint starts than it was doing a quarter ago — they do what defect counts alone cannot: show whether the process itself is improving, independent of how many defects happened to ship in a given month for unrelated reasons.

The Cost of Treating Every Escaped Defect the Same Way

There is a broader economic backdrop worth naming, even though it does not isolate requirements-specific costs on its own. The Consortium for IT Software Quality (CISQ), an industry standards body focused on software quality measurement, estimated the total cost of poor software quality in the United States at $2.08 trillion in its 2020 report and at $2.41 trillion in its 2022 report — a increase driven primarily by rising costs from operational software failures and unsuccessful IT projects. CISQ's methodology aggregates categories like technical debt, failed projects, and legacy system maintenance rather than breaking costs down by whether the originating defect was a testing miss or a requirements gap, so it should not be read as direct evidence for the taxonomy in this article. What it does establish, credibly, is that the downstream cost of shipping the wrong behavior — regardless of which discipline is nominally responsible — is large and growing, which is the correct frame for why getting the root-cause diagnosis right matters beyond the discomfort of a single postmortem.

Chart 2 — Cost of poor software quality in the United States, CISQ estimates

Year Estimated total cost of poor software quality in the US
2020 $2.08 trillion
2022 $2.41 trillion

Source: CISQ, "The Cost of Poor Software Quality in the US" — 2020 report, 2022 report

What this shows: The headline figure rose by roughly $330 billion between the two most recent CISQ reports, driven by growth in operational software failure costs and unsuccessful IT project costs. This is real, sourced data reflecting CISQ's aggregate methodology across the US economy; it is included here as business-context evidence that the cost of getting product behavior wrong in production is substantial and rising, not as a claim about what fraction of that cost is attributable to requirements gaps specifically, which CISQ's public reporting does not break out.

The connection to this article's argument is qualitative rather than statistical: every defect that reaches production because a requirement was silent on the behavior that mattered carries the same downstream costs as a defect that reached production because a test was skipped — incident response time, customer-facing damage, engineering time diverted to a fix instead of new work, and in regulated domains like fintech and healthcare, potential compliance exposure. The cost does not care which department gets blamed in the postmortem. Only the fix does.

Where This Framework Does Not Belong

A framework this focused on precision has an obvious failure mode if applied without judgment: forcing exhaustive, testable acceptance criteria onto work that is still genuinely exploratory destroys the value of that work rather than protecting it.

Early-stage product discovery — a prototype built to learn whether users want a feature at all, a spike to validate a technical approach before committing to it, an A/B test variant designed to be thrown away regardless of outcome — should not be gated behind the same Definition of Ready used for a refund flow shipping to every paying customer. Demanding precise, testable criteria for "figure out whether users would use a bulk-export feature if we built one" misunderstands what that work is for. The entire point of a discovery spike is that the team does not yet know the correct behavior, and writing detailed acceptance criteria for behavior nobody has validated produces false precision, not real clarity — a team that writes exhaustive criteria for a throwaway prototype has usually convinced itself the prototype is more finished, and more load-bearing, than it actually is.

The practical distinction is whether the work is going to be relied upon by real users making real decisions with real consequences if it behaves incorrectly, or whether it exists to answer a question before that reliance is created. A feature flag'd experiment shown to five percent of users for two weeks, explicitly framed as disposable, does not need the same rigor as the permanent refund flow every support agent will use going forward. The risk is not zero in the exploratory case — a broken experiment can still corrupt data or damage a user's experience — but the correct response to that risk is a lighter, faster form of review (a quick sanity check on obvious failure modes, a rollback plan, a blast-radius limit) rather than the full Definition of Ready checklist built for durable product surfaces.

Teams that get this distinction wrong tend to err in one of two directions. Some apply heavyweight requirements discipline everywhere, including exploratory work, and discovery slows to the point that the organization stops learning quickly, which is its own form of risk — shipping the wrong durable feature because nobody validated demand cheaply first. Others apply the loose, exploratory mode of working to everything, including permanent revenue-facing flows, on the theory that "we move fast here" — which is how a refund ticket with three happy-path acceptance criteria ships to production as though it were a two-week experiment rather than a permanent capability every support agent will rely on indefinitely. The judgment call belongs to product leadership, made explicitly rather than by default: is this piece of work meant to be relied upon, or meant to produce a decision about what to build next? The answer determines which side of the Definition of Ready line it falls on.

Scaling the Discipline: Startup, Scale-Up, Enterprise

The mechanics of "write testable acceptance criteria, review them collaboratively, gate them with a Definition of Ready" stay constant across company size, but the amount of ceremony that should surround them does not, and applying enterprise-weight process at a five-person startup is its own failure mode, just as applying startup-weight looseness at an enterprise creates the ambiguity problem at a much larger and more expensive scale.

Dimension Startup (roughly 1–15 engineers) Scale-up (roughly 15–80 engineers) Enterprise (formal requirements process already in place)
Where refinement happens Inside normal backlog grooming; often the founder or a single PM plus one or two engineers A recurring, lightweight three-amigos-style session per feature area or squad Embedded as an added checkpoint inside the existing BRD/sign-off process, at the point a requirement becomes a sprint-sized ticket
Who plays the "testing perspective" role Often an engineer consciously wearing that hat; dedicated QA may not exist yet A QA engineer or QA lead attached to the relevant squad A QA lead or test architect with authority to block a ticket independent of the business sign-off chain
Definition of Ready enforcement A short, memorized checklist; enforcement is social (a team norm) rather than tooled A tracked checklist as a ticket template or a required field before sprint entry A formal gate tied to the tracking tool's workflow state, often with an audit trail for compliance purposes
Primary risk if skipped Small team size means gaps get caught informally more often, but there's no backstop when they don't, and nobody is watching the pattern over time Cross-team ambiguity — two squads building adjacent features interpret a shared term differently, as in the inventory example above Requirements are detailed but were never reviewed by anyone testing-minded, so the document is precise on paper and still ambiguous in execution
Defect root-cause tagging Rare; usually informal ("yeah, that ticket was vague") without a system of record Increasingly valuable as team count grows past what one person tracks by memory Often required for audit and compliance reasons regardless of whether it's used to actually change process

The pattern worth internalizing across all three rows is that the discipline does not need to look identical to be effective. A five-person startup enforcing a two-minute verbal Definition of Ready check before every ticket enters a sprint is doing the same job as an enterprise's formal sign-off gate, at a scale appropriate to five people. What breaks the discipline is not informality — it's inconsistency, where the check happens on tickets someone happens to think are risky and gets skipped on the ones that turn out, after the fact, to have needed it most.

What Product Managers Should Ask Before Calling a Ticket Ready

The following questions are meant to be run by the person writing or approving a ticket, before it goes to refinement, as a self-check that reduces how much gets caught downstream.

  • Have I described what happens on the second time this action occurs, not just the first — a second refund, a second edit, a second submission?
  • Have I named every role or account type that can reach this feature, not only the role I was picturing when I wrote the ticket?
  • Is there a word in this ticket — "current," "active," "valid," "recent" — that could reasonably mean two different things to two different engineers? If so, have I defined it explicitly?
  • If this feature can be used by two people or two processes at the same time, have I said what should happen, or have I explicitly scoped concurrent use out for this iteration?
  • What should happen when the obvious dependency (a third-party API, a database write, a payment processor) fails partway through? Have I written that down, or am I assuming engineering will "handle it reasonably"?
  • If I handed this ticket to someone who wasn't in my head when I wrote it, could they write a test case without needing to ask me what I meant?
  • Is this ticket describing durable, relied-upon behavior, or is it genuinely exploratory work that doesn't need this level of precision yet? Have I been honest with myself about which one it is?

What Engineering and QA Leads Should Push Back On

The corresponding discipline on the other side of the table is knowing when to send a ticket back rather than accepting it and filling the gaps with assumptions during development — which is how implicit, undocumented decisions get made by whoever happens to be writing the code that day, rather than by the person accountable for the business outcome.

  • A ticket describing only the primary actor, with no mention of who else can reach the same feature.
  • A ticket where the acceptance criteria could be satisfied by two different, mutually exclusive implementations, and nobody has noticed.
  • A ticket that names a happy path and no failure path for any external dependency the feature relies on.
  • A ticket where "should," "properly," "correctly," or "as expected" appears in an acceptance criterion without a definition attached — these words describe an outcome without specifying it.
  • A ticket large enough that it's actually several distinct behaviors bundled together, where the missing edge cases are being hidden by scope rather than absent by oversight.
  • A ticket where the answer to "what should happen here" is a business decision, not a technical one, and it's being left to engineering to decide by default during implementation.

Pushing back on any of these is not obstruction. It is the same function the "testing" perspective plays in the three-amigos framing: asking what could possibly happen, before the cost of finding out shifts from a five-minute refinement conversation to a production incident, a postmortem, and a customer-facing consequence.

Frequently Asked Questions

Does this mean QA teams are never actually responsible for escaped defects? No. Genuine testing failures — a known scenario that was skipped, a regression that wasn't caught because a test was quarantined and never revisited, manual testing that didn't follow the documented test plan — are real and belong to QA and engineering process, not to product. The point of the taxonomy in this article is to separate those cases from requirements failures accurately, not to shift blame wholesale from one department to another.

How do we retroactively apply this to defects we've already logged without root-cause tags? Don't try to relabel a large historical backlog; the value is forward-looking. Start tagging new postmortems using the diagnostic questions above, and revisit only defects still active or recently closed where the ticket and discussion history are fresh enough to assess honestly. A partial dataset from the last quarter is more useful than a guessed-at relabeling of the last two years.

Isn't writing this level of detail into every ticket going to slow the team down? It slows down ticket-writing and speeds up everything downstream — less rework, fewer clarifying Slack threads mid-sprint, fewer defects that require an incident response and a second, more careful pass at the same feature. Teams that adopt a real Definition of Ready typically report that refinement takes somewhat longer per ticket and that overall cycle time from "ticket created" to "shipped and stable" improves, because far less time is lost to mid-sprint discovery that the ticket didn't say what everyone assumed it said.

What if the product manager and engineering lead genuinely disagree about whether a ticket is ready? That disagreement, surfaced before development starts, is the system working as intended — it is far cheaper than the same disagreement surfacing after a defect ships. A Definition of Ready checklist works best when it gives both sides a concrete, shared reference to argue from ("criterion six isn't testable because...") rather than a subjective standoff about whether the ticket "feels" clear enough.

Should QA be involved in every single refinement session, even for small teams? Not necessarily as a separate calendar commitment, but the testing perspective — the habit of asking "what else could happen here" — needs to be present in some form on every ticket that will be relied on by real users. On a small team without dedicated QA, an engineer can deliberately hold that perspective during refinement rather than only during test-writing. What doesn't work is skipping the perspective entirely and hoping test design catches gaps that refinement should have caught first.

Where does this leave automated testing and AI-assisted test generation — don't they reduce the impact of this problem? They reduce the cost of testing execution, not the accuracy of what gets tested. An automated suite, or an AI tool generating test cases from a ticket, still needs a specification to test against; both will faithfully encode whatever ambiguity or omission exists in the source ticket, often with more apparent confidence than a human tester would, because generated test names and assertions look precise even when the underlying acceptance criteria weren't. Faster or more automated execution of the wrong test scope does not catch a requirements gap — it just reaches the wrong conclusion more quickly and at greater scale.

The QAtronic Perspective

Most of QAtronic's engagements start with a conversation about test coverage, automation, or release velocity, and a meaningful share of them end up, part way through, at a conversation about how tickets get written before they ever reach a tester. That shift usually happens because coverage and automation improvements plateau against a defect rate that keeps producing a similar pattern: correctly executed tests against an incompletely specified requirement. When that pattern shows up, the highest-leverage change is not another testing tool. It's involving a QA perspective earlier — during refinement, not only during test design — and treating acceptance-criteria review as a deliverable in its own right, alongside test planning and execution. Where that fits into an existing engagement, QAtronic works from the artifacts a team already has — its tickets, its Definition of Ready if one exists, its recent postmortems — rather than proposing a new process from scratch.

A Question Worth Taking Back to Your Team

The next time a defect reaches production and the instinct in the room is to ask how QA missed it, ask a different question first: was the behavior that broke ever written down, anywhere, in language precise enough that two people would have tested it the same way? If the honest answer is no, the postmortem has one useful job left to do, and it isn't reviewing the test plan. It's rewriting the acceptance criteria the way they should have been written the first time, and deciding, as a team, what has to be true before the next ticket like it is allowed to enter a sprint at all.

That decision — what "ready" actually requires, and who has the standing to say a ticket isn't there yet — is a product decision before it is a QA decision. Treating it as one is the fix.

Recent posts

August 29, 2026
SLA Pricing: Set Uptime Promises From Real Incident Data
August 29, 2026
Why 'Testing Gaps' Are Usually Requirements Gaps
August 29, 2026
The Dashboard Was Right. The Decision It Drove Was Wrong