Judgment·8 min read

When NOT to reach for AI

AI can write most of your code, but it cannot own the decisions that make code correct, safe, or legal. This is a playbook for the moments when reaching for the model is the expensive move — where its confidence is a liability and your judgment is the actual product. Learn to recognize these situations by feel, before they cost you a weekend or a customer.

The core test: can you verify the output cheaper than you can produce it?

AI is a bet that checking an answer is cheaper than deriving it. That bet pays off for a React component you can see render, a SQL query you can run against a scratch database, or a regex you can throw twenty test strings at. It collapses the moment verification is as hard as the original problem: a concurrency fix where the bug only appears under load, a financial rounding rule where 'looks right' and 'is right' differ by a cent that becomes a lawsuit, a migration that silently drops rows. Before you prompt, ask what it would take to actually know the output is correct. If the honest answer is 'ship it and find out,' you are not saving time — you are borrowing it at a bad rate.

Anything where being wrong is expensive and being wrong is silent

The dangerous quadrant is high-cost, low-visibility errors. Auth and access control belong here: an AI-generated middleware that checks `if (user)` instead of `if (user && user.id === resource.ownerId)` passes every happy-path test and leaks every other user's data in production. So do money movements, permission boundaries, data-deletion paths, and anything touching PII retention. These aren't harder to write than a dashboard — they're harder to notice when wrong. Use AI to draft the shape, but treat the security-relevant lines as something you personally read, reason about, and cover with an adversarial test (log in as user B, request user A's invoice, assert 403). Don't outsource the reasoning to the thing that produced the code.

javascript
// AI-plausible, quietly broken: any logged-in user can read any order
app.get('/orders/:id', requireAuth, async (req, res) => {
  const order = await db.order.findById(req.params.id)
  res.json(order) // never checks order.userId === req.user.id
})

When the real work is deciding, not typing

A huge share of engineering is choosing: is this a queue or a cron job, one service or three, Postgres or a vector store, should this even exist? AI will happily answer any of these — fluently, confidently, and without owning the consequences. It optimizes for a plausible response to your prompt, not for the thing you actually need six months from now when the schema has to change. The tell is when you catch yourself asking the model 'what should I do here?' about an architectural fork. That's not a knowledge gap it can fill; it's a judgment call that depends on your traffic, your team, your budget, and your risk tolerance — context the model doesn't have and won't ask for. Use it to enumerate options and pressure-test yours, never to make the call for you.

When you can't specify it, AI can't build it

Vibe coding hides a hard requirement: you still have to know what you want, precisely. AI amplifies the clarity of your spec — including its absence. If your prompt is 'build a notifications system,' you'll get a thousand lines implementing a notifications system, and you'll spend longer discovering it batches wrong, dedupes wrong, and has no retry semantics than if you'd written the three sentences that pin those decisions down. The failure isn't the model's; it's that you delegated a decision you hadn't made. When you notice your prompt getting vaguer as the feature gets more important, stop. That's the signal to close the editor and write the spec — in plain English, on paper — first. AI is a bad place to discover your requirements and a great place to execute them once found.

Debugging you don't understand yet

The most seductive misuse is pasting a stack trace and shipping whatever fix comes back. Sometimes it's right. But AI pattern-matches symptoms to plausible causes, and a plausible cause is not the actual cause. When the model 'fixes' a race condition by adding a try/catch, or resolves a failing test by loosening the assertion, it has made the symptom disappear while leaving the disease — often making it harder to find later. The rule: never accept a fix you can't explain back in one sentence. If you can't say why it was broken and why this fixes it, you haven't debugged, you've gambled. Use AI to generate hypotheses ('what could cause this?') and to help you instrument the code, then confirm the mechanism yourself before you accept the patch.

Fast-moving, high-stakes, or niche facts — the knowledge-cutoff trap

Models are frozen at a training cutoff and will state outdated APIs, deprecated flags, and old pricing with total confidence. For anything versioned or regulated — a library's current API surface, a cloud provider's this-quarter pricing, a framework's breaking changes, tax rules, GDPR specifics — the model's fluency is exactly what makes it dangerous. It will invent a Stripe method signature that hasn't existed for two major versions and never flag the uncertainty. For these, the source of truth is the official docs, the changelog, and the actual package.json in your repo, not the model's memory. A good rule: if the answer could change between model versions and being wrong breaks the build or the law, verify against a primary source. Use AI to point you at where to look, not as the thing you quote.

Taste, tone, and anything a human will judge

AI writes competent, median prose and median UI — which is precisely the problem for anything meant to feel like you. Your product's core copy, your error messages, your pricing page, your README's opening line, the personality of your CLI: these are where average is a losing position, because the whole point is to not sound like everyone else who prompted the same model. The default AI voice is recognizable now, and users clock it. Let it draft, then rewrite in your voice — or write the load-bearing sentences yourself and let it fill the boilerplate around them. The same holds for judgment-heavy design: what to leave out, which feature to cut, where to add friction on purpose. Restraint is a taste decision, and taste is the one thing you can't prompt for.

A working rule of thumb

Reach for AI when the task is well-specified, cheaply verifiable, reversible, and low-stakes if wrong — the vast, boring middle of software where it genuinely 10x's you. Hold back when the task is a decision, when errors are silent, when correctness is expensive to check, when the facts move faster than the model's memory, or when the output carries your name and taste. The senior move isn't using AI for everything or refusing it on principle — it's knowing which of these two piles the current task falls into, and being honest when a fun prompt is really you avoiding a hard decision. That honesty is the whole skill.

Takeaways

  • If verifying the output is as hard as producing it, AI isn't saving you time — it's deferring the cost.
  • The danger zone is errors that are both expensive and silent: auth, money, permissions, data deletion.
  • Never accept a fix or a security-relevant line you can't explain back in one sentence.
  • AI amplifies the clarity of your spec, including its absence — write the spec before you prompt.
  • For versioned or regulated facts, the docs and your package.json are truth; the model's memory is a guess.
  • Architecture calls and taste calls depend on context the model doesn't have — those stay yours.