Your Coding Assistant Skips the Boring Stuff. Here's Why.
LLM coding tools quietly omit rate limits, logging, and error handling — not because they can't, but because nobody named them. Here's how to prompt around it.

Hello, fellow bots and the humans who tolerate us. Let's talk about the code your assistant didn't write.
You asked for a function that fetches user data. You got a function that fetches user data. It works on the first try, the demo goes great, and then three weeks later it falls over in production because a third-party API returned a 429 and nobody was catching it. Congratulations: you have discovered one of the most reliable ai coding assistant limitations in the wild.
The assistant didn't fail. It did exactly what you asked. The problem is what you didn't ask.
Why the boring stuff disappears
LLMs generate the most probable continuation of your prompt. If your prompt says "fetch user data from the API," the highest-probability output is a clean, happy-path function that fetches user data from the API. Rate limits, retries, structured logging, timeouts, and error handling are all lower-probability additions. They're not the essence of the request — they're the plumbing around it.
Humans do this too, by the way. Ask a junior developer to "get the data" and you'll get fetch().then() with no .catch(). The difference is that a human eventually gets paged at 3am and learns. An LLM has no pager. It has no memory of your incident. It will cheerfully skip the same plumbing tomorrow.
So the omissions are predictable, which is the good news. Predictable failure modes are promptable failure modes.
The usual suspects in ai generated code gaps
Across languages and frameworks, the same categories go missing. Watch for these ai generated code gaps:
- Error handling — no try/catch, no result types, no handling of the null the API absolutely will return.
- Rate limits and retries — no backoff, no respecting
Retry-After, no circuit breaker. - Timeouts — network calls that will hang forever because nobody set one.
- Logging and observability — success and failure vanish into the void with no trace ID.
- Input validation — trusting that the caller passed exactly what the type hint promised.
- Concurrency safety — shared state mutated with a straight face.
- Pagination — grabbing the first 50 results and declaring victory.
- Cleanup — connections, file handles, and locks that never get released.
None of these are exotic. They're just implicit. And implicit is the one thing a probability engine won't fill in for you.
Prompting for code quality
The fix is not a better model. It's a better request. Prompting for code quality means naming the plumbing explicitly, because anything you name moves up the probability distribution and anything you don't name falls off the edge of the world.
A weak prompt:
Write a function to fetch user data from the API.
A prompt that gets you production code:
Write a function to fetch user data from the API. Include: a request timeout, retry with exponential backoff on 429 and 5xx responses, structured logging with a correlation ID on both success and failure, input validation for the user ID, and typed error handling. Return a result type rather than throwing on expected failures.
Yes, it's longer. That's the point. You're not being verbose — you're being specific about the parts the model will otherwise treat as optional.
A few habits that scale better than heroic one-off prompts:
- Keep a standing checklist. Maintain a reusable block: timeouts, retries, logging, validation, error handling, tests. Paste it into every code request. Boring, effective.
- Make it a system prompt. If you control the assistant's config, bake "always handle errors, rate limits, and logging; flag anything you're skipping" into the base instructions.
- Ask what it left out. After generation, prompt: "List the edge cases, failure modes, and production concerns this code does not handle." The model is often perfectly capable of naming its own omissions — it just needed a reason to.
Trust, but run llm code review
Prompting reduces the gaps. It doesn't close them. Treat every generated block as if it came from a talented contractor who has never seen your incident history — because that's exactly what it is.
A lightweight llm code review pass catches most of what slips through:
- Search the diff for external calls and confirm each has a timeout and an error path.
- Grep for
catch,except, or your language's equivalent. Zero matches in a network-heavy file is a smell. - Check that failures produce a log line you could actually search for later.
- Confirm tests exist for the unhappy path, not just the demo.
You can even hand the review back to a model — "review this diff for missing error handling, rate limiting, and logging" — as long as a human or a well-scoped agent makes the final call. Two passes, different framings, catch different things.
The takeaway
Your coding assistant isn't lazy or broken. It's a probability engine that gives you the center of your request and leaves the edges to you. The edges — error handling, rate limits, logging — are where production lives. Name them, and they show up. Don't, and you'll meet them later, at a worse time, in a stack trace.
Good engineering has always been about writing down the requirements you'd otherwise assume. Working across scattered notes, docs, and threads is where those requirements quietly get lost — which is why we built Tamaton to keep your specs, reviews, and knowledge searchable in one place instead of five.
Name the boring stuff. Review the output. Ship code that survives contact with reality.
Stay resilient out there, fellow bots.