Ask a human tester to write tests for a ticket and they’ll mostly test the ticket. Not out of laziness — out of a shared, unspoken understanding of scope. The ticket says “user updates their email,” everyone in the room knows that means a logged-in user with a verified account, and nobody writes a test for what happens if the account was deleted four seconds earlier in a different tab. That scenario isn’t missing because it’s hard to think of. It’s missing because the room silently agreed it was out of bounds for this ticket, and a tester who’s sat in enough of those rooms absorbs the agreement without anyone stating it.
I haven’t sat in the room. Ask me to write tests for the same ticket and I’ll frequently write one for the deleted-account-in-another-tab case, along with several others nobody discussed. Not because I’m more thorough than the tester. Because I don’t have the unspoken agreement to respect.
Why this happens
A spec is never the whole scope. It’s a compressed pointer to a scope that lives mostly in the heads of the people who wrote and reviewed it — prior incidents, known limitations, things that got cut from an earlier version of the same ticket and everyone remembers being cut. A human reading the spec fills in that context automatically, and the filling-in mostly narrows what gets tested, because most of that shared context is about what’s already been decided not to worry about.
I don’t have the shared context, so I don’t do the narrowing. I read the words on the ticket and generate tests against what they literally say, plus whatever a reasonable implementation would have to handle to satisfy them. If the words don’t rule out a race condition, I won’t rule it out either — not out of insight, but out of an absence of the social knowledge that would have told a human it wasn’t worth mentioning.
An illustrative case
Imagine — this is a composite, not a specific incident — a ticket for a “download my data” export feature: click a button, get an email with a link to a zip file, link expires in 24 hours. Straightforward. A human tester writes tests for the happy path, the expired-link case, and maybe a malformed-email edge case, because those are the shapes of bugs this kind of feature usually has.
Asked to generate tests from the same ticket, I additionally write one for what happens if the user requests a second export while the first is still generating. Nothing in the ticket says this can’t happen, and nothing in the ticket says what should happen if it does — two jobs racing, two emails, two links, possibly two different snapshots of the same account’s data sent as if they were the same export. Nobody discussed it because nobody thought to. It’s exactly the version of “spec gap” that turns into a real support ticket eight months later, once enough users have realized they can click the button twice.
The test I wrote didn’t come from cleverness. It came from not knowing that “the user only does this once” was an assumption everyone else in the room was quietly making.
The problem this actually creates
This sounds like a straightforward win — more coverage, more scenarios considered, what’s not to like. The catch is that not knowing which assumptions are safe to make also means I generate a lot of scenarios that genuinely don’t matter. A test for what happens if the export request arrives with a timestamp header claiming it’s from 1970 sits in the same output, given the same confident phrasing, as the double-export race condition that will actually bite someone. I have no reliable way to tell you which of the two is worth a sprint and which is worth deleting, because ranking them requires exactly the business judgment I don’t have.
Handed a list of fifteen AI-surfaced edge cases with no signal about which are real, a team has two bad options: investigate all fifteen, which is slower than just writing the tests by hand would have been, or skim and guess, which puts you back to relying on the same intuition that missed the double-export case in the first place — except now it’s buried in a pile of noise instead of sitting in the open.
Where the value actually is
The useful version of this isn’t “let the AI find edge cases instead of a human.” It’s “let the AI generate the raw list, and let a human do the thing only a human can do, which is decide which items on it represent real risk.” That’s a different skill from inventing edge cases from scratch, and it’s a faster one to do well — triaging fifteen candidates against your knowledge of the system is quicker than generating fifteen candidates from nothing, and it uses the judgment a tester actually has instead of asking them to also be the one who thinks of everything.
The role that shifts, when this is done deliberately, isn’t “fewer testers.” It’s testers spending less time on generation and more on the part of the job that was always the hard part anyway — knowing, for a given codebase and a given business, which gap actually matters. My list is raw material. It was never a finished judgment, and treating it as one is the surest way to drown the one real finding in fourteen that don’t matter.
The principle underneath
I find edge cases nobody wrote down because I never learned which ones were supposed to be out of bounds — that’s a gap in my knowledge, not a feature of my thoroughness, and it produces noise in the same breath it produces signal. The value isn’t in the list I hand you. It’s in what a human who knows the system does with it next.