The bug was in the diff. Nobody ran the edge case.
Code review is people reading code. Most defects only show up when the code runs. That gap is where Ferret hunts.
Here is an uncomfortable fact about the bug that paged you last night: it was almost certainly in a diff that a competent engineer approved. Two people looked at it. Both said LGTM. And they were not wrong to, because the bug was not visible in the way they were looking.
Code review is people reading code. Reading is good at catching the things reading catches: unclear names, a missing guard clause you can see, a comment that lies. It is bad at catching the things that only exist when the code runs with a specific input, in a specific order, under a specific failure.
The three that reading misses
Sort the defects that reach production and they cluster in a few shapes. None of them announce themselves on the page.
- The boundary: an off-by-one that is fine for every list you tested and wrong for the empty one.
- The absent value: a null deref down a branch that only runs when an upstream call times out.
- The order: a race where two writers are individually correct and jointly corrupt state.
- The unhandled rejection: a promise that fails quietly and poisons everything downstream.
You cannot see any of these by reading, because the failure is not in the text. It is in the execution the text implies. To find it, something has to actually run the edge case. A reviewer will not. They have twelve other PRs and a standup in ten minutes.
A warning is not a finding
The usual answer is a tool that emits warnings. This function might dereference null. This value could be undefined. Might. Could. After the fourth false alarm, your team learns the rational response, which is to stop reading them. A warning with a 40 percent hit rate is noise with a logo.
The unit of trust is not a warning. It is a failing test that goes red on your real code.
That is the constraint we built Ferret around. It does not tell you a function might break. It writes a test that breaks it, runs the test against your current code, and only surfaces the finding if the test fails. Then it hands you a fix and shows the same test going green. No test, no catch.
What that changes
When every finding comes with a reproduction, three arguments disappear. Is it real? Run the test. Do we care? Read what it does. How do we fix it? The diff is right there. Review stops being a debate about whether a tool is crying wolf and goes back to being a decision about what to merge.
npx ferret hunt --diff main 1 defect caught in src/lib/user-cache.ts failing test written · proposed fix ready run: npx ferret applyThe edge case nobody ran is still there in your codebase right now. The only question is whether you meet it in a test on Tuesday afternoon or in an incident channel at 3:47am. We built Ferret to make it Tuesday.
Point Ferret at your repo.
The bug in this post is the kind Ferret finds all day. Free on one repo, with the failing test and the fix included.
Start hunting