Skip to content
Aventheon

Your AI-generated prototype is going to production: a hardening checklist

October 12, 2026

AI coding tools are very good at producing a demo. The screens render, the happy path works, and a founder can show it to a customer by Friday. That is real value, and there is nothing wrong with starting this way.

The trouble starts when the demo gets users. A prototype is optimized to look right on the happy path. Production is mostly about the unhappy paths: the malformed request, the expired session, the user who should not see that record, the traffic spike, the 3 a.m. failure nobody noticed. Generated code tends to be thin there, because nobody asked for it.

This is a checklist for closing that gap. It assumes a typical web app with a database and some third-party APIs. Go through it in order, because earlier items make later ones safer.

1. Authentication and authorization

Authentication (who are you) and authorization (what may you do) are different, and prototypes often have only the first, if that.

  • [ ] Use a maintained auth library or provider rather than hand-rolled login code
  • [ ] Passwords hashed with a modern algorithm (argon2 or bcrypt), never stored plain
  • [ ] Sessions expire, and logout actually invalidates them
  • [ ] Every data-access path checks that the current user may touch that record

That last one deserves a test. The most common prototype flaw is an endpoint like /api/invoices/123 that returns the invoice to anyone logged in, regardless of owner. Try it: log in as user A, request user B's record by ID.

curl -s -H "Authorization: Bearer $USER_A_TOKEN" \
  https://staging.example.com/api/invoices/$USER_B_INVOICE_ID
# Expect 403 or 404, not the invoice

2. Secrets

  • [ ] No keys, tokens or passwords in the repository, including history
  • [ ] Secrets come from environment variables or a secrets manager
  • [ ] .env files are in .gitignore
  • [ ] Any key that was ever committed or pasted into a chat prompt is rotated

Scan for what is already there:

# Example with gitleaks, scans full history
gitleaks detect --source . --log-opts="--all"

Rotation matters more than deletion. A key removed from the latest commit is still in history and may already have been copied.

3. Input validation

Treat every input as hostile: request bodies, query strings, headers, uploaded files, webhook payloads.

  • [ ] Validate shape and types at the boundary with a schema library (Zod, Pydantic, or your framework's equivalent)
  • [ ] Use parameterized queries or an ORM; never build SQL by string concatenation
  • [ ] Escape output rendered into HTML; do not inject raw user content
  • [ ] Limit upload size and restrict file types
  • [ ] Do not pass user input to shell commands or file paths unchecked

A boundary schema is a few lines and removes a whole class of bug:

import { z } from "zod";

const CreateOrder = z.object({
  productId: z.string().uuid(),
  quantity: z.number().int().min(1).max(100),
});

const parsed = CreateOrder.safeParse(await req.json());
if (!parsed.success) return Response.json({ error: "Invalid input" }, { status: 400 });

4. Tests

You do not need full coverage on day one. You need tests on the things that would hurt most if they broke.

  • [ ] Tests for auth and permission rules (the cross-user check above, as an automated test)
  • [ ] Tests for money, data deletion and anything irreversible
  • [ ] One end-to-end test of the main user journey
  • [ ] Tests run in CI on every pull request, and a failing test blocks the merge

Be wary of generated tests that merely restate the implementation. A test that mocks everything and asserts the mock was called proves little. Read a few and ask what bug each one would actually catch.

5. Observability

If you cannot see it, you cannot fix it.

  • [ ] Structured logs with a request ID, and no passwords or tokens in them
  • [ ] Error tracking that alerts a human (Sentry or similar)
  • [ ] A health-check endpoint and uptime monitoring
  • [ ] Alerts on error rate and latency, going to somewhere people look
  • [ ] Database backups, and a restore you have actually tried

The restore test is the one people skip. A backup you have never restored is a hope.

6. Dependency pinning and review

Generated code pulls in packages freely, and some suggestions are outdated or, occasionally, names that do not exist or are look-alikes of real ones.

  • [ ] Lockfile committed (package-lock.json, pnpm-lock.yaml, poetry.lock or equivalent)
  • [ ] Versions pinned, installs in CI use the lockfile (npm ci, pnpm install --frozen-lockfile)
  • [ ] Every dependency reviewed: does it exist on the official registry, is it maintained, do you need it
  • [ ] Vulnerability scanning on a schedule (npm audit, pip-audit, or a service)
  • [ ] Remove unused packages
npm ls --depth=0        # what did we actually install?
npm audit --omit=dev    # known issues in production dependencies

7. Rate limits and abuse protection

Any public endpoint can be hammered, whether by an attacker or a buggy client.

  • [ ] Rate limit login, signup, password reset and any endpoint that sends email or SMS
  • [ ] Limit request body sizes
  • [ ] Cap expensive operations (search, exports, calls to paid APIs like LLMs) per user
  • [ ] Set timeouts on all outbound calls

Unmetered calls to a paid API are a quiet way to get a large bill. Put a per-user budget in front of them.

8. The boring remainder

  • [ ] HTTPS everywhere, with secure cookie flags
  • [ ] CORS limited to the origins you actually use
  • [ ] Debug mode and verbose error pages off in production
  • [ ] Separate staging and production environments with separate credentials
  • [ ] A written deploy process and a rollback you can do in minutes

How to work through it

Do not try to fix everything at once. Order by blast radius: authorization and secrets first, since those lead to data exposure. Then validation and backups. Then tests and monitoring, so that future changes stay safe. Rate limits and polish follow.

One useful habit: for each item, have someone other than the person who built the feature verify it. The tool that wrote the code is a poor judge of whether the code is safe, and the same applies to the person who prompted it.

When to bring in help

Some of these are an afternoon each. Others, like retrofitting authorization across an app that never had it, can be a real project. If you are not sure where your prototype stands, a free 30-minute Triage is a good place to start; bring the repo description and we will tell you what we would check first. For a faster signal, the instant check will flag the obvious gaps in a few minutes.