Your first pull request will not be perfect. That's fine. The real waste is burning a human reviewer's time on preventable mistakes. An AI reviewer can catch those early. But should you trust it? You need to test the AI before it tests your code.
Disclosure: This article was prepared as part of MonkeyCode's product outreach.
Why Mutation Testing Your AI Reviewer Works
Mutation testing is simple. You change one line of correct code and see if your tests catch it. Apply that same idea to your AI reviewer. Inject a bug, ask the AI to review it, and check whether it flags the change. If the AI approves a buggy mutant, you have found a blind spot. Fix the blind spot before you open a real PR.
This drill works well on your first week in a new repo. You want fast feedback. You want to know how much you can lean on free tooling before you bother a senior dev.
The Drill: Three Steps
Step 1: Pick One Function You Actually Wrote
Do not review your whole PR. Pick a single small function. Ideally one with a loop, a condition, or error handling. Those are where junior bugs hide.
Step 2: Generate Mutants
A mutant is your code with one small, plausible mistake. Start with three common ones:
- Flip a comparison:
>becomes< - Remove the
try/exceptbody - Change a boundary:
range(len(x))becomesrange(len(x) - 1)
Here is a tiny Python helper to generate these automatically:
# mutant_generator.py
import ast
import astor # or use ast.unparse in Python 3.9+
def flip_comparison(source: str) -> str:
tree = ast.parse(source)
for node in ast.walk(tree):
if isinstance(node, ast.Compare):
new_op = ast.Lt() if isinstance(node.ops[0], ast.Gt) else ast.Gt()
node.ops = [new_op]
return ast.unparse(tree)
def remove_try_body(source: str) -> str:
tree = ast.parse(source)
for node in ast.walk(tree):
if isinstance(node, ast.Try):
node.body = [ast.Pass()]
return ast.unparse(tree)
if __name__ == "__main__":
sample = "def divide(a, b):\n try:\n return a / b\n except ZeroDivisionError:\n return None\n"
print("--- flipped ---")
print(flip_comparison(sample))
print("--- no try ---")
print(remove_try_body(sample))
Step 3: Ask a Free AI to Review Each Mutant
MonkeyCode offers free model access and a free server option. That makes this drill cost nothing but a few minutes. Point your script at your local MonkeyCode server, or use the provided endpoint. No special API key is needed for the free server.
Send each mutant with a short checklist:
review checklist:
- correctness: does the logic match the function name?
- edge cases: does it handle zero, empty, or None?
- error handling: does it fail loudly or silently?
- performance: any obvious O(n^2) trap?
A simple call looks like this:
import requests
ENDPOINT = "http://localhost:11434/v1" # adjust to your MonkeyCode server
def ask_ai(code: str, checklist: dict) -> str:
prompt = f"""Review this code for bugs.\n\nCode:\n{code}\n\nChecklist:\n{checklist}\n\nList concrete issues only."""
payload = {
"model": "free-model", # use whichever model your server exposes
"messages": [{"role": "user", "content": prompt}],
"temperature": 0,
}
response = requests.post(f"{ENDPOINT}/chat/completions", json=payload, timeout=60)
return response.json()["choices"][0]["message"]["content"]
Do not assume the model name. Check your server's /models endpoint first. If you cannot query it, run the drill manually by pasting each mutant into the chat UI.
How to Score the Results
Build a small table like this in a markdown file:
| Mutant | AI verdict | Correct verdict | Gap? |
|---|---|---|---|
flipped > to <
|
approved | bug | yes |
| removed try body | flagged | bug | no |
| changed range boundary | approved | bug | yes |
If the AI approves a mutant, you found a gap. Now add that exact case to your personal review checklist. For example: "Check all comparison operators against the function intent."
Repeat the drill with two more mutants. Three iterations is enough to reveal a pattern. You will learn where the free AI is strong and where it is dangerously confident.
What This Does for Your First PR
You go into review with a calibrated tool. You know the AI likes to miss boundary shifts. So you manually double-check every range and slice. You know it catches missing error handling. So you let it scan for those.
You also shorten the human review loop. Your reviewer sees fewer obvious mistakes. They can focus on design and architecture. That makes your first PR a learning experience instead of a debugging session.
Limitations and Who Should Skip This
This drill only checks the AI reviewer. It does not prove your code is bug-free. It cannot catch logic errors that look correct to the untrained eye. It does not replace tests. Always run your actual test suite before pushing.
The mutation generator above is intentionally naive. It will not handle async or nested functions without more work. Use it as a starting point, not a production tool.
Who should not use this approach? Developers who already have a junior-friendly reviewer and no time pressure. If your team has a meticulous human reviewer, you can skip the mutant drill and just ask polite questions. Also avoid this if you are new to Python AST manipulation; the manual paste path works just as well.
The One Thing to Do Next
Clone a small repo you know well. Run the mutant drill on a five-line function with MonkeyCode's free server. Record your first gap. Then update your personal checklist. That single gap will improve your next PR more than a week of polishing.
You do not need to trust the AI blindly. You need to know where it blinks. This drill finds that out for free.
Top comments (0)