the cheapest model call is the one you delete
updated Jun 21, 2026
Riichi’s daily puzzle started as a Claude-generated hand. The answer was never the model’s — shanten and the optimal discard come from a library, and that part I’d defend to the end — but the model invented the hand and wrote the explanation. Reasonable. Then I spent weeks defending that one generation call.
Defending it grew an entire apparatus. A six-attempt retry loop to skip degenerate hands. A dedup module, because the model kept converging on the same “instructive textbook hand” and one day literally served yesterday’s puzzle. Per-attempt nonce seeding to force variety. A database table to cache the day’s result. A cron job to pre-warm that cache so no real visitor ate the cold-generation latency. A streamed skeleton to hide the wait behind a spinner. Every piece was a sensible patch on the piece before it.
Then I stopped and asked what all of it was for, and the honest answer was: to make an LLM behave like a curated list. So I wrote the curated list — hand-authored puzzles in a version-controlled file, picked by day index. That deleted the retry loop, the dedup module, the cron, the cache round-trip, and the skeleton in one commit. And it was strictly better for a learning tool: I control difficulty, I can order the puzzles easy-to-subtle, the content reviews in a PR diff, and validation moved into CI. The cost line that used to read “negligible” now reads zero, with no latency left to hide.
Caching, cron pre-warming, retry loops, dedup guards — those are all scaffolding around a model call. When the scaffolding outweighs the call, that’s the tell. The win isn’t a cheaper prompt; it’s noticing that a static list or a plain computation does the job, and deleting your own clever infrastructure. Knowing when to take the model out is the same skill as knowing where to put it.
the hub · warm terminal