Thuta Learning
ExercisesDevOps & Toolsbeginner

Exercise: Deploying AI-Generated and Vibe-Coded Apps Safely

What you'll walk away with

  • Explain the core ideas behind Exercise: Deploying AI-Generated and Vibe-Coded Apps Safely
  • Read the diagram and trace how a request or data flows through the architecture
  • Explain what this means for your own project's decisions

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.

text
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

javascript
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));
You should see
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 - ConfigCloud & Deployment

Cloud & Deployment Glossary — Common Terms

TermMeaning
DeploymentThe process of taking code from your machine and making it run on a server where real users can access it.
ProductionThe live environment that real users interact with, as opposed to a testing or development environment.
StagingA pre-production environment that mirrors production as closely as possible, used for final testing before release.
HostingA service that provides the servers and infrastructure needed to keep an application running and reachable online.
ServerA computer (physical or virtual) that runs software and responds to requests from clients over a network.
ClientThe program — often a web browser or mobile app — that sends requests to a server and displays the response to a user.
DomainA human-readable address, like example.com, that identifies a website instead of its numeric IP address.
SubdomainA prefix added to a domain, like blog.example.com, used to organize or separate parts of a site.
DNSDomain Name System — the internet's directory service that translates domain names into IP addresses.
IP AddressA numeric label, like 192.0.2.1, that identifies a device on a network so data knows where to go.
HTTPHyperText Transfer Protocol — the rules browsers and servers use to request and send web content.
HTTPSHTTP encrypted with TLS, so data traveling between browser and server can't be read or altered in transit.
TLSTransport Layer Security — the encryption protocol that secures data sent over a network connection.
CertificateA digital file that proves a server's identity and enables encrypted HTTPS connections to it.
Static SiteA site made of pre-built HTML, CSS, and JS files that are served as-is, with no server-side logic per request.
Dynamic ApplicationAn app that generates content on the fly per request, often using a server, database, or both.
Environment VariableA configuration value set outside the code, so the same code can behave differently across environments.
SecretA sensitive value, like an API key or password, that must never be exposed in code or client-side files.
BuildThe step that compiles, bundles, or otherwise transforms source code into the files that actually get deployed.
Cloud ComputingRenting computing resources — servers, storage, databases — from a provider instead of owning physical hardware.
IaaSInfrastructure as a Service — a provider rents raw virtual servers and networking, and you manage the rest.
PaaSPlatform as a Service — a provider manages servers and runtime, so you deploy code without managing infrastructure.
SaaSSoftware as a Service — a complete, ready-to-use application delivered over the internet, like email or CRM tools.
VPSVirtual Private Server — an isolated virtual machine on shared physical hardware, giving you root-level control.
ServerlessA model where code runs in provider-managed, auto-scaling functions and you're billed only for actual execution time.
Edge ComputingRunning code physically close to the user, at edge locations, to reduce latency instead of one central server.
CDNContent Delivery Network — a network of edge servers that caches and serves static content close to each visitor.
OriginThe original server that holds the authoritative version of content, which a CDN fetches from when its cache misses.
CacheA temporary copy of data stored somewhere fast to access, so future requests don't have to redo the original work.
Object StorageA storage system for files like images, videos, and backups, accessed as whole objects rather than a filesystem.
Persistent StorageStorage that keeps its data after a server restarts or a container is recreated, unlike temporary in-memory storage.
Managed DatabaseA database whose provider handles setup, backups, patching, and scaling, so you mainly just use it.
CI/CDContinuous Integration/Continuous Deployment — automatically testing and shipping code changes whenever they're pushed.
Preview DeploymentA temporary, isolated deployment of a specific change, used to test it before it reaches production.
DockerA tool for packaging an application with everything it needs into a portable container image.
ContainerA lightweight, isolated package that bundles an app with its dependencies so it runs the same way anywhere.
RegistryA storage service for container images that platforms pull from when deploying.
Load BalancerA component that distributes incoming requests across multiple servers so no single one gets overwhelmed.
Horizontal ScalingHandling more load by adding more server instances, rather than making one server bigger.
Vertical ScalingHandling more load by giving a single server more CPU, memory, or disk, instead of adding more servers.
Auto ScalingAutomatically adding or removing server capacity based on real-time demand.
StatelessA design where a server keeps no memory of previous requests, so any instance can handle any request.
QueueA holding area for tasks waiting to be processed, used to handle work asynchronously without blocking a request.
WorkerA process that pulls tasks off a queue and executes them separately from the main request-response cycle.
WebhookAn automated HTTP callback that one system sends to another when a specific event happens.
IdempotencyThe property where repeating the same operation produces the same result as doing it once.
Health CheckAn automated request a platform sends to confirm an app is running and able to serve traffic.
MonitoringContinuously tracking an app's health and performance so problems are caught before users report them.
LoggingRecording events and errors as an app runs, to help diagnose problems after the fact.
ObservabilityThe broader ability to understand a system's internal state from its external outputs — logs, metrics, and traces together.
MetricsNumeric measurements of a system over time, like request count, error rate, or response latency.
TracesA record of a single request's path through a system, showing where time was spent across each service.
IAMIdentity and Access Management — the system controlling who and what can access which resources.
CORSCross-Origin Resource Sharing — the browser rule controlling which other domains a web page is allowed to request data from.
RollbackReverting a deployment to a previous known-good version, usually done quickly after a bad release.
High AvailabilityA system design goal where the app stays up and reachable even if individual components fail.
RPORecovery Point Objective — the maximum amount of data loss (measured in time) acceptable after an outage.
RTORecovery Time Objective — the maximum acceptable time to restore service after an outage.

Production Deployment Checklist

ItemWhy it matters
Build passesA failing or warning-filled build often means the deployed code isn't what you think it is.
Tests passPassing tests are the fastest signal that a change didn't silently break existing behavior.
Environment variables configuredEvery required env var must be set in the production environment itself, not just in your local .env file.
Secrets not committedA secret pushed to a git repository stays recoverable in history even after you delete it later.
Database migration reviewedAn unreviewed migration can lock tables, drop columns, or lose data on a live database.
HTTPS configuredTraffic without HTTPS can be intercepted or altered, and modern browsers flag it as unsafe.
Domain correctA misconfigured DNS record can point production traffic at the wrong environment or nowhere at all.
Error handling existsWithout it, a single unexpected input can crash the app or leak internal details to a user.
Logging enabled without secretsLogs are essential for debugging, but logging a password or API key turns your log storage into a leak.
Monitoring enabledWithout monitoring, an outage is discovered from user complaints instead of an alert.
Backup strategy exists and has been testedA backup that's never been restored is unproven — verify recovery works before you need it in an emergency.
Rollback plan existsKnowing 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 callsWithout them, one slow or overused external dependency can exhaust your resources or your bill.
CORS scoped correctlyAn overly permissive CORS policy lets any website make authenticated requests to your API on a user's behalf.

Easy traps

  • 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.
  • Never assume that working on localhost means it will work in production -- environment, network, database, and security differences can all bite.

Exercise

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.

You'll know it worked when: 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' ]