For any app that reads its settings from the environment
Forget one variable on a deploy, and every email link points at localhost.
You built something people use, and somewhere in it is a line like process.env.APP_URL || "http://localhost:3000". It made local development easy. In production it means a missing variable stops nothing: the app starts, says it is healthy, and runs on the default.
1 · How it happens
The fallback that made development easy.
Agents write
||and??defaults because they keep the app running on a laptop where a value is missing. The same line runs in production. A variable renamed in the hosting dashboard, a new preview environment, a second region, a rebuild: each can start your app without the value, and the code does not complain. It substitutes.2 · What it costs
A healthy deploy with dead links.
Password-reset emails link to
localhost:3000. Sign-in redirects and payment callbacks go to a host nobody can reach. Where the default is a secret or a setting rather than an address, production runs on the value written in your source code. And where the code notices a missing key, logs a warning and carries on, the user sees “sent” or “saved” for something that never happened.3 · Why you haven't seen it
Nothing on your side goes red.
The deploy succeeds, the health check answers, and your own testing happens on the setup where every variable is set. The person who sees the failure is the user holding a dead link, and many of them never write in.
What the free check shows you
Findings like these, for every gap in your app: how serious it is, what is wrong, where it is, and what it costs you if it stays.
The application origin at lib/email/links.ts:6 falls back to a development default when SITE_URL is unset, rather than failing.
A deploy that forgets one variable is not detectably broken: the service boots, answers, and quietly builds every absolute link, redirect and callback against the wrong host until a user reports it.
Evidencelib/email/links.ts:6
At src/lib/mail.ts:22 a missing RESEND_API_KEY is handled by logging a warning and returning a success result to the caller.
The user is told the operation completed when nothing was sent, stored or charged, and the only trace is a warning line nobody is watching.
Evidencesrc/lib/mail.ts:22
The code reads 23 distinct environment variables and validates none of them at startup; the first check of STRIPE_WEBHOOK_SECRET happens inside the request that needs it.
The service reports itself healthy while a dependency it needs is unconfigured, so the user sees a confirmation screen for an action (a mail, a payment, a model call) that never took place.
Evidenceapp/api/stripe/webhook/route.ts:9
Run the free check tonight.
It runs in your coding tool, on your machine, in a few minutes. What reaches us: counts, rule names and file paths with line numbers. Never your source, your data or a secret value. Your report page is kept 7 days behind an access code. Exactly what it runs →
Add it, sign in, ask.
claude mcp add --transport http prodready https://mcp.prodready.si/mcp --scope user- Add it. Run it in a terminal.
- Sign in. In Claude Code type
/mcp, pick prodready, choose Authenticate. A browser page asks for your email; type the 6-digit code we send (or click its link), then approve. No password, no card. - Ask. Type “check my app”.
Send it to your laptop.
Built in Lovable, Replit or Base44, with no coding tool on your laptop? Connect the project to GitHub, open that folder in Claude Code or Cursor, then add the line above. The step-by-step guide →
After the report: when you want the gaps closed the way serious software teams close them, the same prodready connection runs the SDLC on your project. How that works →