Friday evening. QA opens the ticket they filed that same morning and reads the developer’s comment: “It’s not a bug. We changed the validation three weeks ago.” That’s it — half a day of work — a test against rules that no longer exist, reproduction, writing up the ticket, discussing it on a call — gone down the drain. And the most galling part: nobody is at fault. QA tested exactly what the documentation said. The documentation just lied.
The scene is recognizable to the point of toothache. Let’s take it apart step by step — from commit to closure.
How a false bug is born
The backend changes a rule: the email field goes from optional to required. The change ships to production, everything works. But the docs live in a separate tool — Notion, Confluence, doesn’t matter — and someone has to go update them by hand. Which means — someone has to remember. Someone forgot.
Three weeks pass. QA sits down to write tests. They don’t live in the code — they live in the documentation, because that’s their map of the territory. They open the spec, see “email is optional,” write an honest test case: submit the form without email, expect success. The test fails. From QA’s point of view this is a bug: behavior doesn’t match the description. They file a ticket.
Then the developer gets pulled in, spends time on “it’s not a bug” — and the circle closes. Two hours of the team’s time went to a problem that doesn’t exist. And this is the mild scenario: its evil twin stands right next to it — when an endpoint was quietly deprecated, QA never learned about it, the regression wasn’t caught, and it rode off to production.
I already mentioned this episode briefly in Developers don’t write documentation for themselves — there it was one argument among several. Here I want to take the thought to its end: the problem isn’t that somebody is lazy. The problem is that the documentation and the code live in different places and are synchronized by willpower.
Removing “don’t forget” from the equation
In Meldoc, documentation is plain .meldoc.md files right in the repository.
You write, commit and push them the same way as code, without switching to a
separate app. The key moment: the docs fix goes into the same PR as the
validation fix.
In our scenario, the developer who makes email required fixes the spec in the same branch. The reviewer sees both changes side by side and won’t let one through without the other. Merge — and synchronization happens automatically on push. The docs don’t lag three weeks behind, because they physically travel with the code.
I don’t want to sell a perpetual motion machine: documentation can still drift from reality if a person deliberately changes the code and leaves the spec as is. But there’s insurance for that case too. You can hook AI review up to the PR: it reads the code diff and comments right in the pull request where the documentation has diverged from the changes. Not a guarantee of full coverage — but an extra chance that the divergence is caught before the merge, not three weeks later through the mouth of QA. This isn’t “staleness is impossible.” It’s “forgetting became harder, and noticing became easier.”
What changes for QA
Now QA opens the current spec before testing and sees: email is required. They write the test case against the current behavior, not the three-weeks-old one. The false ticket is never born. No need to walk over to the developer asking “so how is this actually supposed to work now” — the answer is already in the docs. QA becomes autonomous, and the developer doesn’t get yanked out of context for an hour-long refresher.
The same disease bites more than just humans. An AI agent reads the same rotten docs and confidently tests nonexistent behavior — more on that in Your AI agent hallucinates because it has nothing to read.
A false bug is cheap on its own. What’s expensive is that it repeats every week in every team where the docs and the code have drifted apart. That’s what always got me about it: the trouble isn’t the mistake — it’s that nobody made one.