{"slug": "15-checks-non-developers-should-keep-in-mind-before-shipping-ai-written-code-to", "title": "15 Checks Non-Developers Should Keep in Mind Before Shipping AI-Written Code to Production", "summary": "Tadashi Shigeoka published a checklist on July 13, 2026, identifying 15 production-readiness checks that non-developers should apply before shipping AI-written code, citing common failures such as double charges, server outages, and audit log inaccuracies. The checklist includes specific review points like graceful degradation, idempotency, timeouts, and health checks, with examples from AI coding agents like Claude Code, Codex, and Cursor.", "body_md": "# 15 Checks Non-Developers Should Keep in Mind Before Shipping AI-Written Code to Production\n\n[Tadashi Shigeoka](/en/author/tadashi-shigeoka/)· Mon, July 13, 2026\n\nWith AI coding agents like [Claude Code](https://code.claude.com/docs/en/overview), [Codex](https://openai.com/codex/), and [Cursor](https://cursor.com/), a non-developer who can read code can stand up a working MVP or internal tool in a day. That part is well-known. The trouble arrives right after: “it seems to work, let’s ship it” is exactly where users get double-charged, servers fall over at 3 a.m., and audit logs start telling lies.\n\nThis is not because the AI is being lazy. AI is good at writing code that meets the spec you stated. It is structurally weak at the parts of production operation you did not think to state. The good news is the parts it misses are almost always the same parts, so they can be turned into a checklist. This post is that checklist, condensed to fifteen items.\n\n### What you will get from this article\n\n- A concrete picture of where AI-generated code tends to fail once it is under production load\n- Fifteen review points a non-developer can run without being a full-time engineer\n- For each point: a Bad code example that AI often produces, and the specific fix to look for\n- Incident scenarios in plain language (double charges, blank screens, leaked PII) rather than abstract “risks”\n- A ready-to-paste prompt that hands this checklist back to your AI so it can review its own diff\n\n## 1. Does It Fail Without Whiting Out the Screen? — Graceful Degradation\n\nGraceful degradation (keeping the system mostly usable when one part breaks) is a baseline for production. AI is great at writing the happy path, and less great at putting a safety net under the exceptions.\n\nHere is the shape of the problem.\n\nThe moment `getUser()`\n\nreturns null because the API is down, `user.name`\n\nthrows, and the entire page renders as a blank white screen. From the user’s perspective, that is indistinguishable from “the service is dead.”\n\n- Check: does each page/route have a fallback UI for errors? (React Error Boundary, Next.js\n`error.tsx`\n\n, and so on.)\n\n## 2. Is It Safe to Press the Same Button Twice? — Idempotency\n\nIdempotency (the property that doing the same operation twice produces the same result as doing it once) matters wherever “at most once” is required, such as charges, sign-ups, and notifications. On flaky networks, client libraries and SDKs that build in retry logic will resend automatically. If the server cannot say “I have seen this request before,” you get double sign-ups, double charges, and audit log entries written twice.\n\nThe incident is easy to picture: a user does not see feedback, taps the button again, gets charged twice, and support hears about it the next morning.\n\n- Check: do your charge/create/notify POSTs deduplicate on an Idempotency-Key or equivalent request ID?\n\n## 3. Do Your External Calls Know When to Give Up? — Timeout\n\nExternal APIs (such as [OpenAI](https://openai.com/), payment providers, and email services) occasionally stop responding. AI-written code often forgets timeouts. If it says only `fetch(url)`\n\n, it is willing to wait forever. One hanging request is bad enough; if it takes down the entire server process, every other user on that instance is collateral damage.\n\n- Check: does every external call carry a\n`timeout`\n\nor?`AbortSignal`\n\n## 4. Does Your Health Check Actually Check Health? — Health Check\n\nLoad balancers and platforms like [Kubernetes](https://kubernetes.io/) poll a health endpoint and drop unhealthy instances. Asked to write one, AI usually delivers this:\n\nThat only proves the Node.js process is alive. If the database is down, if Redis is down, this still returns 200, so the load balancer keeps sending real traffic to a broken box. That is the worst kind of failure: the outage is invisible from the outside.\n\n- Check: does\n`/health`\n\nping the database and any critical dependency before returning ok?\n\n## 5. Never Trust Input — Boundary Validation\n\nTypeScript types only exist at compile time. Values entering the running server may not match their declared types. There are five boundaries worth defending: HTTP request bodies, URL parameters, environment variables, values read from the database, and responses from external APIs.\n\n- Check: is runtime validation (\n[zod](https://zod.dev/),[valibot](https://valibot.dev/), and so on) applied at every boundary where data enters your system?\n\n## 6. Do Your List APIs Have an Upper Bound? — Unbounded Query\n\nA “return all users” endpoint feels fine when there are ten users. In production with 100,000 users, it eats memory and takes the process with it.\n\nTypical incident: sales tries to export to a spreadsheet, hits your CSV export endpoint, and thirty seconds later the server dies from out-of-memory.\n\n- Check: does every list API clamp the result to a maximum (say, 200) and offer pagination (cursor or offset)?\n\n## 7. Have You Considered Concurrent Edits? — Race Condition\n\nTwo admins editing the same user at the same time is a scenario AI does not think about. It writes the naive “read, mutate, save” pattern.\n\nThis also poisons your audit trail: an entry claiming “the user was viewer before this change” becomes false in hindsight. It stings most where the operation is one you may need to explain later, such as permission changes.\n\n- Check: do sensitive entities carry a\n`version`\n\ncolumn or`updated_at`\n\n, and do you enforce optimistic locking via`If-Match`\n\nor the equivalent?\n\n## 8. Do Error Messages Leak Internals? — Information Disclosure\n\nPiping raw exceptions into the response ships stack traces, SQL, table names, internal paths, and library versions to the client, and thus to any attacker.\n\n- Check: does production return only generic messages (“internal error”) while the detail stays in server logs?\n\n## 9. Are You Putting PII in the URL? — PII in URL\n\nA URL like `GET /`\n\nsits in [[email protected]](/cdn-cgi/l/email-protection)&token=xxxx[CDN](https://developer.mozilla.org/en-US/docs/Glossary/CDN) access logs, browser history, and the [Referer](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Referer) header attached to outgoing requests to third-party sites, all in plaintext, effectively forever. AI is happy to shove anything into a query string if that is what will make the code run.\n\n- Check: no email, phone number, token, or date of birth in URL query strings. Move them into the POST body or the\n`Authorization`\n\nheader.\n\n## 10. Do You Distinguish Retryable Errors from Fatal Ones? — Retry Semantics\n\nWhen an upstream API returns [429 (Too Many Requests)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/429), AI-written code often flattens it into something like:\n\n400 means “the client sent something invalid, retrying will not help.” A 429 collapsed into a 400 turns “please slow down and try again” into “give up.” Every client’s retry policy stops working correctly.\n\n- Check: does your handler preserve the upstream status and the\nheader when passing the error back?`Retry-After`\n\n## 11. Do Logs Say Who and Which Request? — Structured Logging\n\n`console.error(\"failed\")`\n\nis useless for tracking down a real incident. Among 100,000 requests, which user hit which endpoint, and when?\n\n- Check: do all logs include a request ID, user ID, and route name in a structured form (typically JSON)? Are you using a structured logger like\n[pino](https://getpino.io/)or[winston](https://github.com/winstonjs/winston)?\n\n## 12. Are You Leaving Auth Defaults Alone? — Auth Defaults\n\nThe rule for security-adjacent code is: do not write it yourself. Even when AI generates the surrounding code, auth belongs to a proven library ([Auth.js](https://authjs.dev/), [Clerk](https://clerk.com/), [Better Auth](https://www.better-auth.com/)). The classic slips to look for:\n\n-\nCookies missing\n\n`HttpOnly`\n\n,`Secure`\n\n, or`SameSite`\n\n-\nPassword reset and login endpoints without rate limiting\n\n-\nAccess or session tokens stashed in\n\n`localStorage`\n\n, which means a single[XSS](https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/XSS)exfiltrates every user’s session -\nCheck: has the AI silently loosened any of the auth library’s default settings?\n\n## 13. The Bare-Minimum Accessibility Bar — A11y\n\nEven for an internal tool, [accessibility](https://developer.mozilla.org/en-US/docs/Web/Accessibility) (the ability to use the app with a keyboard, a screen reader, and so on) is a feature, not a nice touch. AI ships visually complete UI and tends to drop the invisible parts.\n\n-\nDoes\n\n`<title>`\n\nchange per route? -\nCan you drive everything by keyboard? (Tab order, focus rings, skip links.)\n\n-\nDo icon-only buttons carry an\n\n`aria-label`\n\n? -\nDo you convey state through more than color alone (red versus green)?\n\n-\nCheck: have you run an automated checker like\n\n[axe DevTools](https://www.deque.com/axe/devtools/)at least once?\n\n## 14. Is UI State Reflected in the URL? — URL State\n\nFilters, search queries, page numbers, sort orders that “disappear on reload” are the workflow bug AI produces in bulk, because it stuffs everything into React [ useState](https://react.dev/reference/react/useState). Then a teammate opens the URL you sent and sees a different screen, and hitting the browser back button wipes your filters.\n\n- Check: does the URL reproduce the current screen when shared? Are filters and pagination stored in URL query parameters or\n[Next.js](https://nextjs.org/docs/app/api-reference/file-conventions/page#searchparams-optional)?`searchParams`\n\n## 15. Hand the Checklist Back to the AI — Self-Review\n\nEvery point above (items 1 through 14, excluding this one) shares a shape: AI is great at writing what you asked for, and weak at the concerns you did not ask about. That leads to the highest-leverage move of all: hand this checklist to the AI, and have it review its own code.\n\nPaste something like the following into Claude Code or Cursor.\n\nRun the AI as the first reviewer, then look at the result with human judgment. AI cannot be trusted as a hundred-point reviewer, but as a “start the review and catch the obvious misses” reviewer, it is more than good enough.\n\n## Closing — What Stays on the Human Side\n\nAI has made the speed of writing code effectively infinite. The design instincts, the sense of where things burn, still live on the human side for now. As a non-developer, the goal is not to become someone who can write everything themselves. The goal is to smell the dangerous patterns and be able to tell the AI, “no, not that one.” The shortest path there is keeping this checklist within arm’s reach, and making sure the AI has it too.\n\n## The 15-Point Pre-Production Checklist (Recap)\n\n- 1 Graceful degradation: per-page fallback UI for errors\n- 2 Idempotency: Idempotency-Key on charge/create POSTs\n- 3 Timeout: AbortSignal or timeout on every external call\n- 4 Health check: pings the database and critical dependencies\n- 5 Boundary validation: runtime schema on every external input\n- 6 Unbounded query: clamp and paginate all list APIs\n- 7 Race condition: optimistic locking via version / If-Match\n- 8 Error messages: no stack traces or SQL in production responses\n- 9 PII in URLs: no email, phone, token, or DoB in query strings\n- 10 Retryable errors: preserve upstream status and Retry-After\n- 11 Structured logs: requestId, userId, and route in every log line\n- 12 Auth defaults: HttpOnly / Secure / SameSite / no localStorage tokens\n- 13 A11y: per-route titles, aria-label on icon buttons, keyboard support\n- 14 UI state in URL: shared URLs reproduce the current screen\n- 15 Self-review: feed this list back to the AI so it grades its own diff\n\nThat’s all from a fifteen-point walkthrough of what non-developers should look at before shipping AI-written code, from the Gemba.", "url": "https://wpnews.pro/news/15-checks-non-developers-should-keep-in-mind-before-shipping-ai-written-code-to", "canonical_source": "https://codenote.net/en/posts/ai-coding-production-checklist-non-developers/", "published_at": "2026-07-13 14:58:19+00:00", "updated_at": "2026-08-01 18:09:36.048358+00:00", "lang": "en", "topics": ["ai-tools", "ai-agents", "ai-safety"], "entities": ["Tadashi Shigeoka", "Claude Code", "Codex", "Cursor", "OpenAI", "Kubernetes"], "alternates": {"html": "https://wpnews.pro/news/15-checks-non-developers-should-keep-in-mind-before-shipping-ai-written-code-to", "markdown": "https://wpnews.pro/news/15-checks-non-developers-should-keep-in-mind-before-shipping-ai-written-code-to.md", "text": "https://wpnews.pro/news/15-checks-non-developers-should-keep-in-mind-before-shipping-ai-written-code-to.txt", "jsonld": "https://wpnews.pro/news/15-checks-non-developers-should-keep-in-mind-before-shipping-ai-written-code-to.jsonld"}}