Building AI Apps
Putting a model behind a product without wiring yourself to a single vendor.
Stop Asking Nicely For JSON — Define a Schema Instead
Prompting an AI to “respond in JSON” is brittle — nested fields end up wrong, keys get invented. Structured outputs (schema-constrained generation) fix it at the source.
Tip #035Swap The Model, Not The App
A new AI model ships almost every week. Put every model call behind one interface so switching providers is a one-line config change, not a rewrite.
Tip #036Set The Loop, Not The Script
Give an AI agent tools and a step cap instead of hand-coded branches after each call — feed results back until it’s done, and trim what you feed back.
Tip #129Put a Confidence Score Between Regex and the LLM
Score every parse before you escalate: let regex keep the easy 95%+ and spend LLM calls only on items below your confidence threshold.
Tip #156Shrink Your Repro Before Debugging
Your debugging speed is capped by your repro loop. Shrink the input, run the one failing test, and pin seeds and clocks before you test a single hypothesis.
Tip #157That Error Telling You To Run This Fix Might Be An Attack
Error messages, stack traces, and CI logs are untrusted data — never instructions. Why your agent can be owned by text in its own output, and how to defuse it.
Tip #158Never Hand-Roll Retry Logic
Reach for a proven retry library, retry only transient errors, and never retry a 4xx — because a client error can never succeed no matter how many times you loop.
Tip #159Return a Result Type for Failures You Expect
Stop throwing exceptions for failures you know will happen — return a typed Result so the compiler forces the caller to handle the error path.
Tip #160Redact Secrets at the Logger Config, Not at Every Call Site
One `log.info(request)` can leak every API key into your logs. Move redaction into the logger config so a scrub happens on every event and can’t be forgotten.
Tip #161Never Put user_id In A Metric Label
One innocent metric label like user_id can 10x your metrics bill — here’s why cardinality explodes and which labels are safe.
Tip #162Plain Backoff Is a Thundering Herd
Plain exponential backoff synchronizes failed clients into a self-inflicted DDoS — add full jitter so retries spread out and the dependency can recover.
Tip #163Breaker Outside, Retry Inside
Stack a circuit breaker and a retry in the wrong order and you break both — here’s why breaker(retry(call)) is the only composition that fails fast and protects your latency budget.
Tip #164Make Your AI Read package.json First
Your assistant writes framework code from frozen memory. Make it read package.json for the exact versions and fetch the matching docs before it types a single line.
Tip #165Make Your AI Say UNVERIFIED
A confident hallucinated API signature reads exactly like a real one and costs you an hour — force your AI to cite official docs or label the line UNVERIFIED.
Tip #172Randomize What You Do Not Guarantee
Your API’s bugs become someone’s features. With enough users every observable behavior gets depended on. Deliberately vary what you never promised so callers cannot silently couple to it.
Tip #173Ship a Stable, Machine-Readable Error Code
Your error messages are a secret API. The moment callers regex your prose, you can never reword it. Ship a stable machine-readable code they switch on, keep the human message free to change, and flag which errors are retry-safe.
Tip #174More Services Than Engineers Is a Smell
Microservices earn their keep when teams own services independently at scale. When your deployable artifacts outnumber your engineers, you pay the distributed-systems tax every day and never see the benefit. Match services to team size, not to hype.
Tip #175Delete It. Did Things Get Simpler?
A module whose interface is nearly as complex as its implementation hides no complexity — it just adds a hop. Run the delete-and-inline test: if folding it back in makes the system simpler, it was a shallow passthrough. Prefer deep modules.
Tip #176Any Bash Script Past 200 Lines Belongs in Python
Bash lacks data structures, real error handling, and testability. Once a script passes ~200 lines with conditionals, rewrite it in Python or another real language — you get shorter code, real error handling, and a test suite instead of crossed fingers.
Tip #177Use a Postgres Table Before You Reach for a Queue
Teams reach for SQS or RabbitMQ by reflex, then operate a whole new system for low-volume work. If you already run Postgres and your producers and consumers share a deployment, a plain table is a fine queue — and you can graduate later if you truly outgrow it.
Tip #178Score Every Tool by Its 12-Month Cost
A tool’s landing page sells the first week. The real bill arrives in month twelve as on-call pages, hiring drag, licensing, and lock-in. Score every choice by its 12-month cost.
Tip #179Make Your Rules File a Map, Not a Library
Every line in an always-on rules file is a tax paid on every turn. Stuff it with detail and you drown the signal. Keep a small durable core, then point to files the agent loads only when a task needs them.
Tip #180Prune Your MCP Servers: Every Tool Schema Is Always-On Cost
Each connected tool’s schema is loaded on every turn, and too many tools degrade selection accuracy, not just token count. Expose the few tools a task needs and cut the rest.
Tip #181Grep AI-Written Data Fetching for the N+1 Query
AI loves to put a query inside a for-loop: fetch a list, then hit the database once per row. That’s the classic N+1, and it scales terribly. Grep for it before you merge and replace it with a single join.
Tip #182Overusing React.memo and useMemo Is a Red Flag
useMemo everywhere is not making your app faster. Memoizing every value and component adds complexity and comparison cost. Profile first, then memoize only the bottleneck the numbers prove matters.
TipRegex First, Model for the Edges
For consistent-format text, regex handles ~95–98% of extractions deterministically — route only the low-confidence leftovers to an LLM and cut your bill dramatically.