Build the mental model
AI coding assistants can generate a working feature, or even a full app, in minutes. That speed is real, but it moves the risk in your workflow to a new place — from writing code to reviewing code you didn't write.
AI generates or changes code
A feature request or prompt turns into generated routes, components, and queries automatically.
A human reviews the actual diff
Not a summary of what changed — the real, line-by-line diff, read as if a stranger wrote it, because one did.
Commit and open a preview deployment
The change builds in an isolated environment that mirrors production without touching it.
Test the preview against real behavior
Click through it, trigger failure cases on purpose, and check the concerns a working demo doesn't prove on its own.
Promote to production
Only after the review and the preview testing pass — never because "it worked when I tried it."
A working preview proves less than it looks like
It tells you almost nothing about whether secrets are exposed, whether authentication is actually enforced, whether database permissions are scoped correctly, or whether the app handles errors, rate limits, and cost sensibly under real traffic.
AI-powered apps add an architecture-specific risk: a frontend calls a backend, which calls an AI API or a local model. The API key for that service must never reach the browser — embedded in frontend code, anyone can extract it from the network tab and abuse it under your account.
Streaming responses, long-running generation tasks, per-request cost, and rate limits all need explicit handling before an AI feature is production-ready, not discovered after it's already live.
AI APP DEPLOYMENT ARCHITECTURE
------------------------------
AI APP DEPLOYMENT ARCHITECTURE
-----------------------------------------
[ User's Browser ]
|
v
[ Frontend (React / Next.js) ] <-- no AI API key here, ever
|
v
[ Backend / API server ]
|
|-- holds the AI API key (server-side env var only)
|
+--> [ AI API / local model ] (OpenAI, Anthropic, etc.)
|
+--> [ Managed Database ]
|
+--> [ Vector Store ] (embeddings for RAG)
|
+--> [ Object Storage ] (uploads, generated assets)Connect it to a real scenario
You used an AI assistant to add a "chat with your data" feature. It generated a backend route, a frontend component, and a database query — and everything works in the preview deployment. Before promoting this build to production, walk it through the same review every AI-generated change needs.
Read the full diff
Open every changed file and read it as if a stranger wrote it, because one did. Don't rely on the AI's own summary of what it changed.
Confirm the API key stays server-side
The AI API key should be read only from a server-side environment variable, and should never appear in any file that ships to the browser.
Check that auth is actually enforced
Confirm the authentication middleware on the new route is wired up and called on every request, not just defined somewhere and forgotten.
Check how the database query is scoped
An assistant asked to "fetch the user's data" will sometimes write a query broad enough to fetch everyone's. Read the WHERE clause, not just the table name.
Trigger a failure on purpose
Disconnect the database or send a malformed request, and confirm the user sees a generic error message, not a stack trace or an internal file path.
Check timeouts, rate limits, and logging
Confirm the AI API call has a timeout and a rate limit attached, and that logs capture enough to debug an issue without recording the API key or full user prompts.
Only once every one of these checks passes does "it worked in preview" actually mean the app is ready for production traffic.
Before Deploying an AI-Generated App
Try the working example
function reviewGeneratedCode(code) {
const findings = [];
const keyPattern = /(sk-[a-zA-Z0-9]{20,}|api[_-]?key\s*[:=]\s*["'][^"']{10,}["'])/i;
if (keyPattern.test(code)) {
findings.push("Possible hardcoded API key found in code");
}
const callsExternalApi = /fetch\(|axios\.(get|post)/.test(code);
const hasAuthCheck = /requireAuth|isAuthenticated|checkAuth|session\.user/.test(code);
if (callsExternalApi && !hasAuthCheck) {
findings.push("Route calls an external API but has no visible auth check");
}
const leaksStack = /res\.(send|json)\(\s*\{?\s*.*err(or)?\.stack/.test(code);
if (leaksStack) {
findings.push("Error handler sends the raw stack trace to the client");
}
const hasCatch = /catch\s*\(/.test(code);
if (callsExternalApi && !hasCatch) {
findings.push("External API call has no error handling");
}
const hasTimeout = /timeout|AbortController/i.test(code);
if (callsExternalApi && !hasTimeout) {
findings.push("External API call has no timeout configured");
}
return findings.length
? findings
: ["No red flags found - safe to proceed to preview deploy"];
}
const riskySnippet = `
const OPENAI_KEY = "sk-abcdefghijklmnopqrstuvwx1234567890";
app.post("/api/chat", async (req, res) => {
const response = await fetch("https://api.openai.com/v1/chat/completions", {
headers: { Authorization: \`Bearer \${OPENAI_KEY}\` },
method: "POST",
body: JSON.stringify(req.body),
});
const data = await response.json();
res.json(data);
});
`;
const cleanSnippet = `
app.post("/api/chat", requireAuth, async (req, res) => {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 8000);
try {
const response = await fetch("https://api.openai.com/v1/chat/completions", {
headers: { Authorization: \`Bearer \${process.env.OPENAI_API_KEY}\` },
method: "POST",
body: JSON.stringify(req.body),
signal: controller.signal,
});
const data = await response.json();
res.json(data);
} catch (err) {
res.status(500).json({ error: "Something went wrong. Please try again." });
} finally {
clearTimeout(timeoutId);
}
});
`;
console.log("Risky snippet findings:");
console.log(reviewGeneratedCode(riskySnippet));
console.log("\nClean snippet findings:");
console.log(reviewGeneratedCode(cleanSnippet));Running reviewGeneratedCode() against both the risky and the clean snippet:
Risky snippet findings:
[
'Possible hardcoded API key found in code',
'Route calls an external API but has no visible auth check',
'External API call has no error handling',
'External API call has no timeout configured'
]
Clean snippet findings:
[ 'No red flags found - safe to proceed to preview deploy' ]5-minute try-it
Extend reviewGeneratedCode() yourself: add a check that flags a database query string containing "SELECT *". Then write a third snippet — one that has an auth check but leaves the API key sitting inside a frontend component file — and confirm the function correctly flags it too.
One important caution
Trusting the AI's own summary of a diff instead of reading the actual changed lines — the summary can miss exactly the line that matters.
Assuming a feature that works in preview is production-ready, when preview testing rarely exercises real load, real rate limits, or real cost.
The Twelve-Factor App - Config — Cloud & Deployment
Cloud & Deployment Glossary — Common Terms
| Term | Meaning |
|---|---|
| Deployment | The process of taking code from your machine and making it run on a server where real users can access it. |
| Production | The live environment that real users interact with, as opposed to a testing or development environment. |
| Staging | A pre-production environment that mirrors production as closely as possible, used for final testing before release. |
| Hosting | A service that provides the servers and infrastructure needed to keep an application running and reachable online. |
| Server | A computer (physical or virtual) that runs software and responds to requests from clients over a network. |
| Client | The program — often a web browser or mobile app — that sends requests to a server and displays the response to a user. |
| Domain | A human-readable address, like example.com, that identifies a website instead of its numeric IP address. |
| Subdomain | A prefix added to a domain, like blog.example.com, used to organize or separate parts of a site. |
| DNS | Domain Name System — the internet's directory service that translates domain names into IP addresses. |
| IP Address | A numeric label, like 192.0.2.1, that identifies a device on a network so data knows where to go. |
| HTTP | HyperText Transfer Protocol — the rules browsers and servers use to request and send web content. |
| HTTPS | HTTP encrypted with TLS, so data traveling between browser and server can't be read or altered in transit. |
| TLS | Transport Layer Security — the encryption protocol that secures data sent over a network connection. |
| Certificate | A digital file that proves a server's identity and enables encrypted HTTPS connections to it. |
| Static Site | A site made of pre-built HTML, CSS, and JS files that are served as-is, with no server-side logic per request. |
| Dynamic Application | An app that generates content on the fly per request, often using a server, database, or both. |
| Environment Variable | A configuration value set outside the code, so the same code can behave differently across environments. |
| Secret | A sensitive value, like an API key or password, that must never be exposed in code or client-side files. |
| Build | The step that compiles, bundles, or otherwise transforms source code into the files that actually get deployed. |
| Cloud Computing | Renting computing resources — servers, storage, databases — from a provider instead of owning physical hardware. |
| IaaS | Infrastructure as a Service — a provider rents raw virtual servers and networking, and you manage the rest. |
| PaaS | Platform as a Service — a provider manages servers and runtime, so you deploy code without managing infrastructure. |
| SaaS | Software as a Service — a complete, ready-to-use application delivered over the internet, like email or CRM tools. |
| VPS | Virtual Private Server — an isolated virtual machine on shared physical hardware, giving you root-level control. |
| Serverless | A model where code runs in provider-managed, auto-scaling functions and you're billed only for actual execution time. |
| Edge Computing | Running code physically close to the user, at edge locations, to reduce latency instead of one central server. |
| CDN | Content Delivery Network — a network of edge servers that caches and serves static content close to each visitor. |
| Origin | The original server that holds the authoritative version of content, which a CDN fetches from when its cache misses. |
| Cache | A temporary copy of data stored somewhere fast to access, so future requests don't have to redo the original work. |
| Object Storage | A storage system for files like images, videos, and backups, accessed as whole objects rather than a filesystem. |
| Persistent Storage | Storage that keeps its data after a server restarts or a container is recreated, unlike temporary in-memory storage. |
| Managed Database | A database whose provider handles setup, backups, patching, and scaling, so you mainly just use it. |
| CI/CD | Continuous Integration/Continuous Deployment — automatically testing and shipping code changes whenever they're pushed. |
| Preview Deployment | A temporary, isolated deployment of a specific change, used to test it before it reaches production. |
| Docker | A tool for packaging an application with everything it needs into a portable container image. |
| Container | A lightweight, isolated package that bundles an app with its dependencies so it runs the same way anywhere. |
| Registry | A storage service for container images that platforms pull from when deploying. |
| Load Balancer | A component that distributes incoming requests across multiple servers so no single one gets overwhelmed. |
| Horizontal Scaling | Handling more load by adding more server instances, rather than making one server bigger. |
| Vertical Scaling | Handling more load by giving a single server more CPU, memory, or disk, instead of adding more servers. |
| Auto Scaling | Automatically adding or removing server capacity based on real-time demand. |
| Stateless | A design where a server keeps no memory of previous requests, so any instance can handle any request. |
| Queue | A holding area for tasks waiting to be processed, used to handle work asynchronously without blocking a request. |
| Worker | A process that pulls tasks off a queue and executes them separately from the main request-response cycle. |
| Webhook | An automated HTTP callback that one system sends to another when a specific event happens. |
| Idempotency | The property where repeating the same operation produces the same result as doing it once. |
| Health Check | An automated request a platform sends to confirm an app is running and able to serve traffic. |
| Monitoring | Continuously tracking an app's health and performance so problems are caught before users report them. |
| Logging | Recording events and errors as an app runs, to help diagnose problems after the fact. |
| Observability | The broader ability to understand a system's internal state from its external outputs — logs, metrics, and traces together. |
| Metrics | Numeric measurements of a system over time, like request count, error rate, or response latency. |
| Traces | A record of a single request's path through a system, showing where time was spent across each service. |
| IAM | Identity and Access Management — the system controlling who and what can access which resources. |
| CORS | Cross-Origin Resource Sharing — the browser rule controlling which other domains a web page is allowed to request data from. |
| Rollback | Reverting a deployment to a previous known-good version, usually done quickly after a bad release. |
| High Availability | A system design goal where the app stays up and reachable even if individual components fail. |
| RPO | Recovery Point Objective — the maximum amount of data loss (measured in time) acceptable after an outage. |
| RTO | Recovery Time Objective — the maximum acceptable time to restore service after an outage. |
Production Deployment Checklist
| Item | Why it matters |
|---|---|
| Build passes | A failing or warning-filled build often means the deployed code isn't what you think it is. |
| Tests pass | Passing tests are the fastest signal that a change didn't silently break existing behavior. |
| Environment variables configured | Every required env var must be set in the production environment itself, not just in your local .env file. |
| Secrets not committed | A secret pushed to a git repository stays recoverable in history even after you delete it later. |
| Database migration reviewed | An unreviewed migration can lock tables, drop columns, or lose data on a live database. |
| HTTPS configured | Traffic without HTTPS can be intercepted or altered, and modern browsers flag it as unsafe. |
| Domain correct | A misconfigured DNS record can point production traffic at the wrong environment or nowhere at all. |
| Error handling exists | Without it, a single unexpected input can crash the app or leak internal details to a user. |
| Logging enabled without secrets | Logs are essential for debugging, but logging a password or API key turns your log storage into a leak. |
| Monitoring enabled | Without monitoring, an outage is discovered from user complaints instead of an alert. |
| Backup strategy exists and has been tested | A backup that's never been restored is unproven — verify recovery works before you need it in an emergency. |
| Rollback plan exists | Knowing exactly how to revert before you deploy turns a bad release into a five-minute fix, not a scramble. |
| Rate limits / timeouts configured for external calls | Without them, one slow or overused external dependency can exhaust your resources or your bill. |
| CORS scoped correctly | An overly permissive CORS policy lets any website make authenticated requests to your API on a user's behalf. |