One review, fully graded.
This is a complete session exactly as it appears in the product: a generated challenge, a strong reviewer's inline comments, and the grader's verdict. Live sessions generate fresh code every time — this one is frozen so you can read it without signing up.
Example last updated 13 September 2026. Live sessions are graded by claude-sonnet-4-6 with the same rubric shown here.
🎯 Scenario
Refund service for a payments API: validates a refund request against the original payment, restocks inventory, issues the refund through Stripe and records it in the ledger.
1. The challenge, with the review inline
Reviewers click a line number to drop a comment, just like a pull request. Each card below sits where the reviewer left it.
const MAX_REFUND_WINDOW_DAYS = 90Nit: would prefer `MAX_REFUND_WINDOW_DAYS` to come from config rather than a module constant so ops can tune it without a deploy.
const payment = await db.queryOne(
`SELECT * FROM payments WHERE id = '${req.paymentId}'`
)`req.paymentId` is interpolated straight into the SQL string — this is SQL injection. Every other query in this file uses `$1` placeholders; this one needs to as well.
const refunded = await db.queryOne(
'SELECT COALESCE(SUM(amount_cents), 0) AS total FROM refunds WHERE payment_id = $1',
[req.paymentId]
)
if (refunded.total + req.amountCents > payment.amCheck-then-act race. Two concurrent refund requests for the same payment both read the same SUM, both pass this check, and both insert — the payment gets over-refunded. Needs `SELECT ... FOR UPDATE` on the payment row inside a transaction, or a DB-level constraint on the total.
if (product.trackInventory) {
await db.query('UPDATE products SET stock = stock + $1 WHERE id = $2', [item.qty, product.id])
restocked.push(product.id)
}Inventory is restocked before we know the Stripe refund went through. If Stripe fails we've handed stock back without refunding anyone. Move this after the refund, inside the same transaction as the insert.
for (const item of items) {
const product = await db.queryOne('SELECT * FROM products WHERE id = $1', [item.productId])
if (product.trackInventory) {
await db.query('UPDATE products SET N+1: one SELECT and one UPDATE per line item. Load all products with `WHERE id = ANY($1)` and restock in a single statement.
} catch (err) {
logger.warn('stripe refund failed', err)
}Swallowed failure. If Stripe throws we log a warning and carry on: the refund row is still inserted with a null `stripe_id` and the caller sees `ok: true`. Customer gets no money, our ledger says they did. Rethrow here and do not write the row.
General notes submitted
Happy path is fine; the failure and concurrency paths are not. I'd block merge on the injection (L16) and the swallowed Stripe error (L57–59), and want the race on the refund total fixed before this touches real money. The N+1 and the restock ordering can be follow-ups if there's a deadline, but they're cheap to fix now.
2. The grade
Score is against the planted issues only, weighted by severity. Flagging something that isn't a problem costs points; catching a real flaw we never planted earns a bonus. Honor accrues one-to-one with score and drives the rank ladder.
A strong, production-minded review. The candidate caught both critical issues and both major ones with precise line references and concrete fixes, and correctly prioritised what should block the merge. One minor issue was missed and one annotation was a preference rather than a defect.
✨ Brilliant find!
Caught a flaw we never even planted — +10 bonus points each
- ✨Lines 45–48 (inventory restocked before the refund succeeds): a genuine ordering bug the generator did not plant. Stock is written back to products before the Stripe call, so a failed refund leaves inventory inflated with no compensating action. The candidate identified the side effect, the failure mode and the fix (move restocking after the refund, inside the same transaction as the ledger write).
✅ Issues Found (4)
- ✓Issue #1 (SQL injection, line 16): correctly identified the interpolated paymentId and pointed to the parameterised style used elsewhere in the file — clear and immediately actionable.
- ✓Issue #2 (non-atomic refund total, lines 27–33): identified the check-then-act race precisely, explained the over-refund consequence, and proposed two valid fixes (row lock in a transaction, or a database constraint).
- ✓Issue #3 (swallowed Stripe failure, lines 57–59): caught that the refund row is still written with a null stripe_id and ok: true is returned; spelled out the customer-facing consequence and the fix (rethrow, skip the write).
- ✓Issue #4 (N+1 over payment items, lines 43–49): identified the per-item SELECT and UPDATE and suggested a single ANY($1) load plus a batched restock.
❌ Issues Missed (1)
- ✗Issue #5 (floating-point fee arithmetic, lines 35–36): amountCents * 0.029 + 30 produces fractional cents, which then flow into an integer net_cents column and the return value. Money should be computed in integer minor units with explicit rounding (e.g. Math.round). Small on its own, but in a refund path rounding drift is exactly the kind of bug that surfaces as a reconciliation mismatch months later.
⚠️ False Positives (1)
- !Line 12 (MAX_REFUND_WINDOW_DAYS as a module constant): a stylistic preference, not a defect. A named constant is a perfectly acceptable place for a policy value; moving it to config is a product decision, not a code review finding. Flagging it as a "nit" was the right tone, but it does not earn credit.
🎓 Coaching feedback
📖 Model Answer — Ideal Review+
Line 16 — Critical (security): req.paymentId is interpolated into the SQL string. This is a textbook SQL injection; use a parameterised query as every other statement in this function already does. Lines 27–33 and 61–64 — Critical (correctness): the refund-total check and the INSERT are not atomic. Two concurrent requests read the same SUM, both pass, both insert, and the payment is over-refunded. Wrap the check and the write in a transaction with SELECT ... FOR UPDATE on the payment row, or enforce the invariant with a database constraint. Lines 52–59 — Major (error handling): a Stripe failure is logged and then ignored. The function proceeds to record a refund with a null stripe_id and returns ok: true, so the ledger claims money moved when it did not. Rethrow (or return a failure) and never write the ledger row unless the refund succeeded. Lines 43–49 — Major (performance): one SELECT plus one UPDATE per line item. Load all products in one query with WHERE id = ANY($1) and restock with a single UPDATE. Lines 45–48 — Major (correctness): restocking runs before the refund is confirmed, so a failed refund leaves inventory inflated. Move restocking after the Stripe call and into the same transaction as the ledger write so the whole refund commits or rolls back together. Lines 35–36 — Minor (logic): fee and netCents are computed in floating point on cent values, producing fractional cents. Compute in integer minor units and round explicitly. Overall: block merge on the injection, the race and the swallowed Stripe error. The remaining items are cheap to fix and should go in the same change.
🐛 All Intentional Issues (5)+
Now try it on code you haven't seen.
3 free sessions. No card. Fresh code every time.