The Bug That Never Threw an Error

For an unknown stretch of time, my checkout page told every customer “order placed successfully” while quietly keeping their entire discount for itself.

Not a crash. Not an error. 200 OK, every single time, because technically, the order was placed successfully. That sentence just left out the part where the discount code someone correctly typed in did absolutely nothing. Full price, charged in silence, with a green checkmark standing guard over the whole thing.

How it happened

Discount codes started out simple, and a little too trusting: the frontend sent a raw discountId straight to POST /orders, and the server took its word for it. That only works if you fully trust the client, and you shouldn’t, so I redesigned it into a proper two-step flow. A code gets typed in, POST /discounts/validate checks it’s real, active, inside its date range, and under its usage limit, then hands back just enough to display the discount line: the type, the amount, nothing about internal ids or usage counts. Then, separately, when the order actually gets placed, POST /orders independently re-validates that same code, inside the same transaction that calculates tax and writes the line items. The frontend’s check is a convenience. The order’s own check is the one that actually can’t be skipped.

Redesigning that meant three separate places needed to agree on where the real discount id was now coming from: the request schema, the calculation logic, and the order transaction itself.

Two of them got updated. The transaction didn’t.

It kept quietly reading a field, the old discountId, that no longer existed anywhere upstream in the new flow. In TypeScript, reading a property that isn’t there doesn’t throw. It just resolves to undefined, and undefined doesn’t stop for anybody. It sailed straight through the calculation, the discount amount came out as zero, and the order wrote successfully at full price. Every customer who entered a completely valid, completely active code got charged as if they hadn’t.

How it got caught, and how easily it almost wasn’t

I’ve got 137 backend tests and a 30-scenario Playwright suite running across Chromium, Firefox, and WebKit for this project. All green, the whole time this bug was live. Every one of those tests checked whether the request succeeded. Exactly one of them happened to also check whether the actual discount amount on the order was correct, and that’s the test that failed. If that single assertion had been written the way most of the others were, confirming the request didn’t error out, this ships indefinitely. A 200 OK doesn’t mean the app told the truth. It means the app didn’t crash while lying to you.

What actually got built while this was hiding inside it

This is a full-stack checkout system, not a toy: real JWT-based authentication with role-based permissions splitting customers from admins, a users/customers table split where a guest checkout gets one row and a registered account gets both, created together in a single transaction. RabbitMQ runs as an honest-to-god separate Node process, not a queue faked inside the API, publishing an order_placed event on every successful order and holding it durably if the consumer happens to be down when it fires. Ownership checks return a 404 instead of a 403 when a customer probes someone else’s order id, on purpose, so a wrong guess and a real record look identical from the outside and never confirm what exists.

On the frontend: React, TanStack Query for data fetching, Zustand for cart state that survives a page reload, React Hook Form handling every input, and shadcn/ui components on top of Tailwind. Every one of those, along with JWT auth itself and Playwright for end-to-end testing, was new to me this month. Nine backend tables, 137 backend tests, 30 browser scenarios, and the whole thing came together in about five days.

The actual lesson

Most of the dangerous bugs in this project weren’t syntax problems. They were decisions that never got made explicitly. Validating a discount code wasn’t really a schema question, it was a decision about who’s allowed to know a discount exists before they’ve used it. Letting a customer cancel their own order wasn’t a button, it was a decision about exactly which status transitions a customer should be trusted to request unsupervised. The code for each feature was the easy part once the underlying decision got made on purpose instead of assumed.

This bug is the sharpest version of that lesson I’ve hit so far. The code wasn’t wrong in any way a compiler could catch. It’s that “the request succeeded” and “the request did the correct thing” are two entirely different claims, and a test suite that only checks the first one will wave a silent, expensive bug straight through a hundred times over and call it a passing build the whole way.

I fixed the transaction. I went back through every test in that file and added real value assertions wherever there’d only been a status check before. Green checkmarks are a start. They were never the whole story.