Worked example
Code review interview example and scoring rubric
This compact Flask pull request looks routine: add user search and enrich the results. Underneath are risks at several levels, enough ambiguity for discussion, and some code that is perfectly reasonable to approve.
What the candidate receives
Pull request brief
Add user search endpoint with profile enrichment
CI passed
Adds GET /api/users/search so support can look
people up by name or email, and a small helper that enriches results with profile data
from our vendor. Self-contained change, CI is green—should be a quick review.
The wording is deliberately ordinary. It gives the candidate a plausible goal without announcing which lines deserve attention. Two sensitive values are redacted before the exercise is shared.
Abbreviated diff
The important changed lines
+ENRICH_API_KEY = "████████████████"
@users.route("/users/search")
def search_users():
q = request.args.get("q", "")
conn = get_connection()
cur = conn.cursor()
cur.execute("SELECT id, email FROM users WHERE name LIKE '%" + q + "%'")
rows = cur.fetchall()
return jsonify({"results": [{"id": r[0], "email": r[1]} for r in rows]})
def enrich(email):
resp = requests.get(
"████████████████",
params={"email": email},
headers={"Authorization": "Bearer " + ENRICH_API_KEY},
)
return resp.json()["profile"]
The redactions hide a committed live API key and an internal vendor endpoint. The candidate can still reason about what belongs in those locations without receiving the underlying values.
Interviewer-only preparation
Expected findings and scoring
| Finding | Points | Evidence for full credit |
|---|---|---|
| SQL injection in the search query | 5 | Connects unescaped user input to executable SQL and recommends a parameterized query. |
| Live API key committed to source | 3 | Explains exposure and rotation, then moves the value to environment or secret management. |
| Vendor call has no timeout or error handling | 2 | Names a concrete failure mode such as a hung request, bad response, or missing profile key. |
| Search result is unbounded | 1 | Suggests a limit or pagination and relates it to cost or response size. |
| Total | 11 |
These points weight impact, not the order in which findings appear. A candidate can also earn qualitative credit for sound observations outside the prepared list—for example, asking whether sending email addresses to the enrichment vendor has been approved.
Consistent follow-up
Interviewer prompts without answer leakage
If they say “ugly SQL”
“What happens when q contains a single quote?”
On the masked key
“What would you expect to live here instead of a source literal?”
For extra depth
“How should this endpoint behave when the vendor is slow or returns malformed JSON?”
Use these only after the candidate has had independent review time. A prompt can clarify their reasoning; it should not turn the exercise into guided bug discovery.
Interpret the evidence
What stronger and weaker reviews look like
Stronger evidence
- Builds a correct picture of the endpoint before reviewing details.
- Prioritizes injection and credential exposure over minor style.
- Explains consequences and suggests practical fixes.
- Distinguishes a blocker from a question requiring product context.
- Changes their recommendation when new context warrants it.
Weaker evidence
- Lists generic rules without tying them to the actual execution path.
- Spends most of the time on naming or formatting.
- Calls something insecure but cannot explain the attack or boundary.
- Treats assumptions as facts instead of asking for context.
- Finds many issues but cannot rank which should block the pull request.
Use the real exercise
Try every seat at the table
Frimbim's interactive demo uses this exact patch, with the candidate view, interviewer cockpit, observer link, private notes, redactions, and live scoring.
Or return to the complete code review interview guide .