The demo goes perfectly. Click, click, click, the feature works exactly as promised, everyone nods, the ticket gets closed. Three days later a real user clicks the fourth button instead of the third one, in an order nobody scripted, and the whole thing falls over. Nothing about the demo was fake. It just tested the one path everyone already agreed to test.
Demo-done and actually-done are different claims
"Done" said out loud in a sprint review usually means: the happy path works, and it looked fine on a screen share. That's a real, useful thing to have proven. It's just a much smaller claim than "this is done," and the gap between the two is exactly where the bugs that show up in week two are hiding — the empty state nobody populated on purpose, the network request that times out, the form submitted twice because someone double-clicked. None of it showed up in the demo, because the demo followed the script, and users don't read the script.
Why the demo keeps passing anyway
This isn't usually dishonesty. A demo optimizes for a specific goal — show that the feature works, in the time allotted, to an audience that isn't trying to break it. Everyone in the room, presenter included, is subtly incentivized toward the happy path: it's faster, it looks better, and finding a bug live in front of the team is nobody's favorite way to spend a sprint review. The dishonesty, if there is any, is more subtle than lying — it's letting "looked fine when I clicked through it" quietly stand in for "done," because nobody wrote down what "done" was actually supposed to mean before the demo started.
A definition of done that isn't a vibe
A useful definition of done is written before the work starts, not inferred from how the demo went. And it needs to say more than "works as expected" — that phrase means something different to everyone reading it. It should name the specific things a feature has to survive to count as finished: what happens when the input's empty, what happens when the network call fails, what happens if a user hits the same action twice, whether there's a test that catches a regression here without someone remembering to click through it by hand again. Write those down as a checklist attached to the ticket, and "done" stops being a feeling in the room and starts being something you can actually check against.
The line that matters: what's out, not just what's in
Most definition-of-done lists that fail do so because they're too vague to be checked ("handles errors gracefully" — which errors, checked how?). The better version is specific enough to fail loudly if it's skipped: "returns a 4xx with a user-facing message if the upload exceeds 10MB, tested in CI." That's checkable. "Handles errors gracefully" isn't — it's a hope wearing a checklist's clothing.
It also has to say what's explicitly out of scope, on purpose, and agreed to by whoever's going to be disappointed if it's missing later. "Not handling concurrent edits in this version" is a fine thing to decide. It's a very different thing when it's a decision everyone made together versus a gap discovered by a support ticket three weeks after launch.
What survives the demo
A definition of done that survives contact with real users isn't the one with the most items on it. It's the one written specifically enough that someone could check it without being in the room when the feature was built, and honest enough to name what's deliberately missing instead of leaving it to be discovered. The demo proves the feature can work. The checklist is what proves someone thought about what happens when it doesn't go according to script — because eventually, it won't.
