A free model can catch local bugs in pull requests—null dereferences, resource leaks, inconsistent error handling—but it will miss security and concurrency issues that need system context. After 12 PRs and one prompt rewrite, false positives dropped from about 60% to 25%; recall barely moved.
I Wired a Free Model to Review Every Diff
Two weeks ago I added a bot to my pull request workflow. My teammates did not ask for it. My manager did not ask for it. I wanted one answer: can a free model actually review code, or is it a fancy spell-checker with better grammar?
The experiment was simple. Every PR I opened got a model review before a human looked at it. I collected every comment, classified it as useful or noise, and compared those findings against the human review that followed. Two weeks. Twelve pull requests. One honest log.
I locked three rules before the first webhook fired:
- Only review the diff, not the whole file.
- Output comments in a structured format with severity levels.
- Never auto-approve or auto-request changes—the model's role was advisory.
The first two rules kept the bot from drowning in files it did not need. The third was the one that mattered for the team: humans still owned the merge. If the model was wrong, it was noise in a thread, not a blocked pipeline.
The prompt I started with was embarrassingly vague:
You are a code reviewer. Review the diff below and provide feedback.
Focus on bugs, security issues, and code quality.
I should have known better. The point was to see what happens when you do not over-engineer the prompt. The answer: chaos. Without a line-number requirement, a severity taxonomy, or a "no issues found" escape hatch, the model filled the vacuum with style nits.
I used MonkeyCode's free model access and a free server option to host the review script—the cost was zero for the whole experiment. Disclosure: This article was prepared as part of MonkeyCode's product outreach.
The script itself is small: a webhook receives the PR diff, sends it to the model with the prompt, and posts structured comments back to the PR. About 150 lines of Python. The hard part is not the code. It is setting expectations so the bot does not flood the thread.
Week One Exposed Local Hits and System-Level Misses
The first review came back with seven comments. One was useful. Six were noise.
The useful one caught a real bug: a null pointer dereference in a function that assumed its input was never null. The model quoted the exact line and suggested a guard clause. That was genuinely good—the kind of local pattern a free model can match without knowing the rest of the service.
The noise was worse than useless. It flagged a variable named temp as unclear—in a three-line function with obvious context. It suggested "adding more error handling" to a function that already had a try-catch around every risky operation. It complained about a missing docstring on a private method nobody outside the class would call.
The worst moment was PR #3. The change touched authentication middleware and compared tokens with == instead of a constant-time comparison. That is a textbook timing attack on an authentication path. Python documents secrets.compare_digest() for exactly this case. A human reviewer caught it in the same session. The model had the diff in front of it and missed it entirely.
Three PRs, three lessons
- PR #1 — null pointer. The model found a real local issue and produced two false positives. Lesson: it can spot local patterns, but it has no sense of proportion.
- PR #3 — timing attack. It missed the security issue and suggested extracting a helper for the comparison. Lesson: it sees syntax, not semantics.
- PR #5 — race condition. The diff added a field to a shared object and updated it from two threads. The model said "looks good to me" with zero comments. The human caught the race in under a minute. Lesson: anything that requires understanding the broader system is outside its reach.
Those three PRs set the ceiling for the rest of the experiment. Local bugs were in play. Security and concurrency were not.
One Prompt Rewrite Cut False Positives to 25%
After the first week I rewrote the prompt. The changes were not subtle.
You are a code reviewer for a Python backend service.
Review the diff below. Follow these rules:
- Only report issues you can point to with a specific line number.
- Ignore style preferences unless they affect correctness.
- For each issue, classify it as: bug, security, performance, or readability.
- Rate your confidence: high, medium, low.
- If you have no high-confidence findings, say "no issues found."
I also added project context: the service handles payments, so security issues should be prioritized; the codebase uses type hints everywhere; the team prefers early returns over nested ifs.
The difference was immediate. False positives dropped from about 60% to about 25%. The model stopped commenting on naming conventions and started focusing on actual problems. It still missed the deep issues, but it stopped wasting everyone's time.
That constraint is not unique to models. Google's code review guidance tells human reviewers to prioritize correctness and design over style nits. The free model needed the same instruction written down, plus a forced "no issues found" escape hatch so it would not invent work.
What changed in practice:
- Every comment had to cite a line number. Vague "add more error handling" disappeared.
- Style-only remarks were banned unless they affected correctness.
tempstopped showing up. - Confidence ratings made it easy to ignore low-confidence noise in the PR thread.
- Domain context (payments, type hints, early returns) shifted attention toward the issues we actually care about.
Side by side, the rewrite does three jobs the original refused to do: it narrows the job title, it forbids comments that cannot be pinned to a line, and it gives the model permission to stay silent.
Precision Rose; Recall Did Not
Week two was better, not transformative. Here is the full breakdown:
| Metric | Week One | Week Two |
|---|---|---|
| Total comments | 34 | 21 |
| Useful comments | 12 | 15 |
| False positives | 20 | 5 |
| Missed critical issues | 2 | 1 |
Over two weeks the model reviewed 12 pull requests with a combined 1,847 lines changed.
- True positives: 27 comments that pointed to real, actionable issues. Of those, 19 were minor (style-adjacent, missing edge cases, redundant checks) and 8 were worth fixing before merge.
- False positives: 25 comments that were wrong or irrelevant. Most were in week one.
- Missed issues: 3 critical problems a human reviewer caught: the timing attack, the race condition, and a missing rollback.
The missing rollback was the most instructive miss. The diff was three lines. Anyone who knew the function could raise between two database operations would have seen an inconsistent state on failure. The model had the diff, the function signature, and the error-handling pattern in front of it. It still said "no issues found."
The model was never wrong in a way that would break the build if you followed its advice. It was also never right about anything that required understanding the system's intent. It is a pattern matcher, not a reviewer.
Where I would use it:
- Catching null pointer risks and missing guards
- Finding resource leaks (it caught an unclosed file handle and a missing cursor close in week two)
- Flagging inconsistent error handling (one function returned
Noneon error while siblings raised) - Catching typos and copy-paste errors
Where I would not:
- Security review — it missed every security issue in the set
- Concurrency analysis — threads, locks, shared state
- Architecture feedback — no sense of the system's shape
- Anything that requires reading the codebase's history — it sees a diff, not a story
The honest conclusion: a free model is a decent junior reviewer who is always available and never gets tired. It is not a senior reviewer. It will not catch the bug that only makes sense after you have worked in the codebase for a year.
Copy the Advisory Setup and Log Useful vs Noise
If you want to run a similar experiment, keep the scope narrow and the model advisory.
- Wire a webhook, not a merge gate. Receive the PR diff, call the model, post comments. Do not let it approve or request changes.
- Ask for one class of bug at a time if the first pass is noisy—nulls, leaks, or error-handling consistency—not "review everything."
- Force structure: line number, severity (bug / security / performance / readability), confidence (high / medium / low), and an explicit "no issues found" path.
- Paste project context into the prompt: domain, type-hint policy, control-flow conventions, what "critical" means for your service.
- Classify every comment for two weeks as useful, noise, or miss, and compare against the human review. That log is the experiment.
Start with a tight prompt and a human still owning the merge. Run it on your next few PRs, keep the same useful-vs-noise log I used, and decide from evidence—not from the first impressive null-check—whether a free model belongs on your review team.
Top comments (0)