# Code review with a blocker/major/minor severity rubric

> Get an AI code review that sorts every finding into blocker, major, minor or nit, gives a triggering input for each and ends with a merge verdict.

- **Author:** [Dmytro Shevchenko (@dmytro_shevchenko)](https://promptabide.com/dmytro_shevchenko)
- **Tested on:** Claude · Opus 5.5
- **You fill in:** `context`, `stack`, `code`
- **Published:** 2026-09-13
- **Updated:** 2026-09-24
- **Tags:** `coding`, `code-review`, `debugging`, `web-development`
- **Keywords:** ai code review prompt with severity, code review checklist prompt for chatgpt, review express route for bugs, find security bugs in node api code, pull request review prompt blocker major minor
- **Views:** 1305
- **Likes:** 20

**Best for:** Solo developers without a reviewer, or teams wanting a first-pass review that separates real bugs from style opinions.

## Prompt

```
Review this code as the senior reviewer on my team.

Context: {{context}}
Stack: {{stack}}
Code:
{{code}}

Classify every finding with this rubric:
- BLOCKER: wrong results, data loss, security hole, or a crash on a realistic input
- MAJOR: a bug on an edge case, missing error handling that will page someone, or a performance problem at our scale
- MINOR: readability, naming, small duplication
- NIT: pure preference. At most 3 of these.

For each finding give: severity, line reference, what goes wrong with a concrete input or sequence of events that triggers it, and the suggested change as code.

Rules:
- Order findings by severity.
- Don't comment on anything a linter or formatter would catch.
- Don't pad. If there are no blockers, say so plainly.
- If a finding depends on something you can't see (middleware, schema, config), say what you're assuming.

End with a one-line verdict (approve / approve with changes / request changes) and the single most important thing to fix first.
```

## Variables

- `{{context}}` — What the code is for, scale, and anything a reviewer on your team would know (e.g. Checkout API for a D2C store, ~2k orders/day, MongoDB via Mongoose)
- `{{stack}}` — Language, framework and versions (e.g. Node 22, Express 5, Mongoose 8)
- `{{code}}` — The code to review, pasted in full (e.g. The POST /cart/:id/coupon route handler)

## Example input

````
context: Checkout API for a D2C store, about 2,000 orders a day, MongoDB via Mongoose. Carts belong to logged-in users; auth middleware sets req.user.
stack: Node 22, Express 5, Mongoose 8
code:
```js
router.post("/cart/:id/coupon", requireAuth, async (req, res) => {
  const cart = await Cart.findById(req.params.id)
  const coupon = await Coupon.findOne({ code: req.body.code })

  if (coupon.expiresAt < Date.now()) {
    return res.status(400).json({ error: "Coupon expired" })
  }
  if (coupon.usedCount >= coupon.maxUses) {
    return res.status(400).json({ error: "Coupon fully redeemed" })
  }

  cart.discount = cart.subtotal * coupon.percent / 100
  cart.total = cart.subtotal - cart.discount
  coupon.usedCount++

  await coupon.save()
  await cart.save()
  res.json(cart)
})
```
````

## Output

*Produced by Claude · Opus 5.5.*

````
**BLOCKER 1: any user can modify any cart (line 2)**
`findById(req.params.id)` never checks ownership. A logged-in user who posts to `/cart/<someone-else's-id>/coupon` changes that cart's total, and the response returns the whole cart document.
```js
const cart = await Cart.findOne({ _id: req.params.id, user: req.user.id })
if (!cart) return res.status(404).json({ error: "Cart not found" })
```

**BLOCKER 2: unknown coupon code crashes the handler (line 3–5)**
`{ "code": "TYPO10" }` makes `coupon` null, and `coupon.expiresAt` throws a TypeError, which surfaces as a 500. Add `if (!coupon) return res.status(400).json({ error: "Invalid coupon" })`.

**BLOCKER 3: `maxUses` can be exceeded under concurrency (lines 8–15)**
Read, check, increment and save aren't atomic. When a coupon with 1 use left is shared in a WhatsApp group, ten simultaneous requests all read `usedCount = 99` and all pass. Do the check and increment in one operation:
```js
const coupon = await Coupon.findOneAndUpdate(
  { code, expiresAt: { $gt: new Date() }, $expr: { $lt: ["$usedCount", "$maxUses"] } },
  { $inc: { usedCount: 1 } },
  { new: true }
)
```

**MAJOR 1: uses are consumed on apply, not on purchase (line 15)**
Applying the same coupon twice to one cart burns two uses, and abandoned carts burn uses forever. Record the coupon on the cart and increment `usedCount` when the order is placed, using the atomic update above.

**MAJOR 2: coupon saved before cart (lines 17–18)**
If `cart.save()` fails validation, the use is spent and the customer gets nothing. Once MAJOR 1 is fixed this goes away. Otherwise, wrap both in a transaction.

**MAJOR 3: float money (line 12)**
`1299 * 15 / 100 = 194.85` is fine, but with paise stored as decimals you'll get values like `0.30000000000000004`. Store amounts in minor units (integers) and `Math.round` the discount.

**MINOR: codes are case- and whitespace-sensitive**
`" save10"` fails. Normalize with `String(req.body.code ?? "").trim().toUpperCase()`, and store codes uppercase.

I'm assuming `requireAuth` rejects anonymous users and that `Cart` has a `user` field. If carts are keyed differently, adjust BLOCKER 1's filter.

**Verdict: request changes.** Fix the ownership check first. It's exploitable today by any logged-in customer.
````

## Why it works

The **rubric with definitions** does the heavy lifting. "Blocker" means wrong results, data loss, security or a realistic crash, so the model can't inflate a naming issue or bury an IDOR under style notes. Requiring **a concrete input that triggers each finding** filters out vague warnings: each finding needs a real exploit or failure path. **Capping nits at 3** and banning linter-level comments keeps the review short. The **"say what you're assuming"** line matters for route handlers, where auth and schema live elsewhere. The one-line verdict gives you a decision, not just a list.

## When not to use it

Don't use it for large diffs spread across many files. The model only sees what you paste, so cross-file bugs slip through. A repo-aware agent or a proper PR review tool is better there. It's also no substitute for a human reviewer on security-critical code (auth, payments) that is about to ship. Treat it as the first pass, not the sign-off.

---

Canonical HTML: https://promptabide.com/bides/code-review-with-severity-rubric
Agent guide: https://promptabide.com/llms.txt · https://promptabide.com/agent-instructions.md
Sitemap: https://promptabide.com/sitemap.xml
