The demo worked. The deployment succeeded. Then the real URL opened to a blank page, login loop, or button that did nothing.
This is not bad luck. Local development and production are different systems. The useful question is not “why is production broken?” It is “which assumption stopped being true?”
Debug the boundary between environments before you debug the whole app.
First, name the exact failure
“Broken in production” is too large to investigate. Write one sentence that another person could reproduce:
- A new user can submit the signup form, but no account is created.
- Google login returns to the homepage instead of the dashboard.
- The first request after a deploy returns 500; the second succeeds.
- Images load on desktop but fail on a real phone connection.
Record the production URL, account state, browser, time, expected result, and actual result. Save the request ID when the platform exposes one. A precise failure turns a nervous search into a comparison.
Check what changed between local and production
Start with the differences, not the code that looked suspicious last night.
Configuration
List every value that can vary by deployment: database address, API key, site URL, cookie domain, email sender, webhook secret, storage bucket, feature flag, and OAuth callback.
The Twelve-Factor App’s guidance on configuration treats these as deployment-specific values rather than constants in source code. That distinction is especially useful in an AI-built app: generated code often invents a fallback value that makes localhost appear healthy while production quietly points somewhere else.
Check presence, name, scope, and format. A variable available during the build may not exist at runtime. A value may contain a trailing newline. A preview credential may have been copied into production. Do not print the secret itself into logs.
Hostnames, HTTPS, and cookies
Localhost is unusually forgiving. Production introduces a real domain, HTTPS, subdomains, proxies, and browser cookie rules.
Inspect the browser’s network panel. Look at the redirect chain and the Set-Cookie response. Confirm the callback URL matches the value registered with the identity provider exactly. Check Secure, SameSite, domain, path, and expiry. Test in a clean browser, because an old development cookie can hide the real behavior.
If authentication works on one hostname but not another, stop changing UI code. The boundary is probably the URL or session configuration.
If the custom hostname itself resolves inconsistently, presents the wrong certificate, or loops between hosts, follow the custom-domain launch diagnosis before debugging the application behind it.
Data and migrations
Your local database contains friendly records. Production begins empty, or worse, with a schema one migration behind.
Confirm the deployed application and the production database agree on table names, columns, constraints, and migration version. Test the empty state. Test the first record. Test a record created under the previous release.
Never repair this by casually running destructive SQL against production. Back up the data, understand the migration, and make the smallest reversible change.
Runtime and filesystem assumptions
A serverless or edge runtime is not your laptop. The operating system, file paths, available APIs, request lifetime, memory, and writable storage may differ.
Search for case-sensitive import mistakes, writes to a local filesystem, long background tasks, native packages, and work that continues after the response without an approved lifecycle mechanism. Check whether the production runtime supports the library the generated code selected.
The build passing proves that a bundle was produced. It does not prove that every runtime path can execute.
External services
Payments, email, storage, maps, AI models, and OAuth providers often have separate test and live modes. Treat each integration as two contracts: your request to the provider and the provider’s callback to you.
Verify the live account, allowed origins, redirect URLs, webhook endpoint, signature secret, permissions, quotas, and regional restrictions. Send one real low-risk transaction through the full loop. Then refund, revoke, or delete the test artifact as appropriate.
Read the production evidence
Do not reproduce the failure ten times without opening the logs.
Good production logs answer: which route ran, which stage failed, what class of error occurred, and which request ties the events together. They should not contain passwords, tokens, full payment details, or private user data.
The Twelve-Factor guidance on logs describes logs as a time-ordered event stream. That is the right mental model. A browser error, server exception, database rejection, and provider callback may be four views of one failed job.
Filter around the recorded time and request ID. Compare one failing request with one successful request. If no evidence exists, add narrow instrumentation around the boundary and redeploy. Guessing is slower.
Reproduce from outside
Use the public domain on a phone, on cellular data, in a private browser, with a newly created account. Follow the path as a stranger would.
Test direct links, refreshes, back-button behavior, expired links, duplicate submissions, and a second account. Production bugs often live in state the founder’s warm browser never creates.
This is why the production checklist for an AI-built app begins with real journeys rather than a tour of the repository.
Change one thing and keep a rollback
Write the current hypothesis before the fix: “The OAuth provider returns to the preview hostname, so the production session is never set.” Make one change that can prove or disprove it.
Deploy, repeat the same reproduction, and inspect the same evidence. If the result changes, record why. If it does not, revert before adding another variable.
Fast debugging is not frantic. It is a short chain of falsifiable claims.
The ten-minute production comparison
When time is short, compare these in order:
- Exact public URL and failing step.
- Browser request, response, and redirect chain.
- Production exception at the same time.
- Environment variable names and runtime scope.
- Database connection and migration version.
- Live versus test credentials.
- OAuth, CORS, cookie, and webhook origins.
- Runtime or filesystem assumptions.
- Empty-account behavior in a private browser.
- Last known good release and rollback path.
If the failure can cost a user money, privacy, or control, stop the affected path while you investigate. A smaller unavailable product is better than a product that completes the wrong action.
Localhost proves that the idea can run in one protected room. Production readiness begins when the same promise survives outside it.
