Welcome to the Showcify Blog

LatestPopular14 articles

The eval graded the answer key, not the document

An extraction eval stayed green for weeks while users watched their data get mangled. The cause: it graded output against our labels, not against the source.

Sep 02, 20269 min readAi
A teacher's red pen resting on a multiple-choice answer sheet marked all correct, beside the original exam booklet lying open and unread.

Charge the quota when you deliver value, not when you accept the request

A visitor gets two free runs. Two dead links later they have used both and received nothing. Where you decrement a quota is a product decision in disguise.

Sep 02, 20265 min readRails
A vending machine coin slot with a coin halfway in and the delivery tray visibly empty.

The eval was grading a prompt we had deleted

Our record/replay eval could not run for two days and reported green throughout, because the scorer kept grading cached results from a prompt we had deleted.

Sep 02, 20269 min readAi
Eval grading deleted prompt

The security gate that defaulted to off

Our docs called the bot challenge mandatory. The env flag behind it defaulted to false in the deploy config, so a routine deploy would ship the gate down.

Sep 02, 20264 min readSecurity
Security Gate that Defaulted Off

Two honest counts, one wrong number: Counter and Validator count different things

The live counter said 5,982 of 6,000. The server said too long. Both were honest; they were counting different strings, and multipart encoding was why.

Sep 02, 20264 min readRails
Counter and Validator count different things

Restorable but not shareable: hiding a token in history.state

A result that survives refresh and the back button but cannot be shared by copying the URL. A forgotten browser API, a header, and one deliberate deletion.

Sep 01, 20266 min readFrontend
Restorable but not shareable

Our rate limits were bypassed by a trailing slash

Five tuned Rack::Attack throttles, all matching the path exactly. Rails normalised the path after they ran, so a dot-json or a trailing slash bypassed every one.

Sep 01, 20265 min readRails
A turnstile in an empty station with an open gap in the railing right beside it.

Treat model output like user input

When strangers write the prompt and the answer renders on your page, you have two untrusted boundaries back to back. The layered posture we settled on.

Sep 01, 20265 min readLlm
Two Trust Boundaries; One System.

Layered bot defense for a public form: honeypot, Turnstile, email verification, and the pieces between

A public, unauthenticated form that writes to the database and sends email is a bot's favorite denial-of-service target. No single control is enough. The point is layering honeypot, Turnstile, rate limiting, and email verification so one layer failing doesn't sink the rest.

Jul 30, 202617 min readrate-limiting
A medieval castle viewed from outside its outer wall. Multiple defensive layers visible in one frame.

Moving JWTs out of localStorage: the surprises waiting in ActionController::API

A JS-readable token in localStorage is one XSS away from account takeover. Moving it into an HttpOnly cookie is the well-known fix but ActionController::API has no cookies, no CSRF, and a few other surprises waiting on the first commit.

Jul 30, 202613 min readsecurity
An old key being lifted off a hook on the wall and locked into a wall safe.

CSP nonces vs. 'unsafe-inline': the silent override that broke our admin UI

A nonce and 'unsafe-inline' in the same CSP directive: the browser silently drops 'unsafe-inline' by spec. Debugging a policy that quietly broke production.

Jul 30, 202610 min readsecurity
Two locks side by side on a door, only one is actually engaged. The other is decorative.

Three Ways VCR Cassette Recording Lied to Me

Three vcr recording behaviors that never raise an error: the config beats the CLI flag, ambient traffic gets taped, and re-records append instead of replacing.

Jul 15, 20265 min readVcr
Three ways VCR cassette lied to me

Cross-provider LLM failover without losing observability

Every LLM provider goes down eventually, so the obvious move is to retry against another one. The naive rescue-and-retry works until your bill spikes and the logs can't tell you how often failover fired or what it cost. Here's the version that keeps the receipts.

Jul 15, 20267 min readLlm
Cross-provider LLM failover without losing observability

How a Private Draft Can Hide Your Live Portfolio From Google

A published portfolio — complete, polished, exactly what search engines want — quietly vanished from Google. The culprit wasn't the live page at all. It was a private draft nobody could see, plus a one-line assumption in our SEO gate that counted it as a duplicate. A short debugging story about canonical URLs, sensible defaults, and the danger of de-indexing both copies of a tie.

Jul 15, 20266 min readEngineering
Showcify blog — a private draft portfolio casting a noindex shadow over a live one