Dev Builds, localhost URLs and Lorem Ipsum: What Ships to Production by Mistake
A development build, an API call to localhost, a staging URL, placeholder text, a console.log full of user data. None of these break a demo, and all of them are live on real sites. How to find each one before your users do.
By UseShipReady
You are serving a development build if your site was started with npm run dev (or next dev, vite) rather than a production build, or if the bundle contains development markers such as React’s dev warnings or Vite’s /@vite/client script. Development builds are slower, larger and more talkative about your code. Fix it by running the production build and start commands, npm run build then npm start or serving the dist folder, with NODE_ENV=production.
AI coding tools are very good at getting an app to run. They are less careful about the difference between "runs on my machine" and "ready for strangers". The result is a family of leftovers that never cause an error, so nobody notices them, but each makes the site slower, less trustworthy or leakier. Here are the seven most common, and how to check for each.
Why do AI-built apps ship these leftovers so often?
None of these are mistakes a careful team makes on purpose. They come from how AI-assisted development works in practice. The assistant’s goal in each session is to make the current thing work, and every item on this list is something that makes a thing work during development: a dev server that reloads instantly, a localhost fallback so the app runs without configuration, filler text so a layout can be judged, a log line to trace a bug. Nothing prompts anyone to undo them, because nothing is broken.
Traditional teams catch this with a release process: a staging review, a checklist, a colleague who asks why the footer says "Your Company". A solo founder shipping from a chat window usually has none of that. The fix is not to slow down but to add one deliberate pass between "it works" and "it is live", which is what the rest of this article is.
1. How do I know if my site is running a development build?
The usual cause is a host where you start the app with a command, such as a VPS, Replit or a container, and the command is npm run dev because that is the one that worked during development. A dev server skips minification, includes debugging code, rebuilds on the fly and often shows detailed error overlays to every visitor.
- View source and look for
/@vite/clientor@react-refresh. Both are Vite dev-server modules and never appear in a production build. - Open DevTools. React DevTools shows a red icon for a development build, and the console fills with React warnings that production builds strip out.
- In a Next.js app,
next devadds a small indicator in the corner of the page and serves unminified chunks. - Check your start command.
package.jsonshould runnext start,node server.jsor a static file server afternpm run build, notdev.
2. API calls to localhost
This one breaks features for every real user while working perfectly for you, because on your machine localhost:3000 is your API. The usual source is a fallback in the code:
// Works locally. In production, if API_URL was not set at BUILD time,
// every visitor's browser calls their own computer.
const API = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000'Browser-exposed environment variables are baked in when the app is built, not when it starts. If the variable was missing on the build machine, the fallback is compiled in permanently. Search your build output for localhost: and 127.0.0.1, and prefer failing the build loudly over a silent fallback.
3. Links to staging and preview environments
The same mechanism with a different URL: an API base pointing at staging.yourapp.com or a *.vercel.app preview deployment. Production users end up writing to a staging database, or a preview deployment with weaker protection is advertised to anyone reading your JavaScript. Grep the build for staging, preview, dev. and your platform’s preview domain.
4. Placeholder content and scaffold branding
Lorem ipsum in a features section, "Your Company Name" in the footer, hello@example.com as the contact address, a testimonial from "Jane Doe", or the tab title still reading "Vite + React". Visitors notice these immediately, and they read as "this business is not real". Search the rendered pages, not just your components, because placeholder text often lives in a CMS or a config file.
5. console.log statements with user data
Debug logging is how assistants trace a bug, and the logs stay after it is fixed. console.log(user) or console.log(response) in browser code prints tokens, email addresses and API responses into the console of every visitor’s browser, and of every browser extension watching it. Strip them at build time and use a proper logger on the server:
// next.config.js — remove console.* (except errors) from production client bundles
module.exports = {
compiler: {
removeConsole: { exclude: ['error'] },
},
}6. Error pages that show a stack trace
A development error page is designed to be as helpful as possible, to you. In production it tells visitors your framework, file paths, library versions and sometimes query text. Framework production modes hide this by default, so a stack trace in production usually means a dev build (see point 1) or a custom error handler that returns err.stack in the response.
7. A 404 page that returns 200
Single-page apps often answer every unknown URL with the app shell and a 200 OK, then render "Page not found" in the browser. Search engines see a successful page and may index thousands of empty URLs. Your missing pages should return a real 404 status. Check with curl -sI https://yourdomain.com/this-does-not-exist, and see canonical tag mistakes that hide pages from Google for the other common way a new site confuses search engines.
A five-minute pre-launch check
npm run build
# Next.js: .next/static Vite: dist
grep -rEl 'localhost:[0-9]+|127\.0\.0\.1|staging\.|\.vercel\.app|lorem ipsum' .next/static dist 2>/dev/null
grep -rc 'console.log' .next/static dist 2>/dev/null | grep -v ':0$'
curl -s https://yourdomain.com | grep -E '@vite/client|react-refresh'
curl -sI https://yourdomain.com/this-does-not-exist | head -1While you are there, check that no secrets shipped alongside the leftovers. A deployment that serves a dev build is often also serving the project folder, which is how an exposed .env file happens.
How UseShipReady finds production leftovers
UseShipReady reads each crawled page and the JavaScript it loads, and reports a development build served to real visitors, a production build pointing at localhost and references to a staging or preview environment. It only counts markers in code the page actually runs, so an article quoting a dev warning, like this one, is not reported.
The same scan covers placeholder content, unedited scaffold branding, JavaScript that logs sensitive data, stack traces on error pages and pages that say not found but return success. The full launch list is in the production readiness checklist.
Frequently asked questions
- Is it bad to run npm run dev in production?
- Yes. A development server is slower, serves larger unminified code, can expose detailed error pages and source to visitors, and is not built to handle production traffic. Build once with npm run build and serve the result with the production start command.
- Why does my production site call localhost?
- Usually because a browser-exposed environment variable was missing when the app was built, so a fallback such as http://localhost:3000 was compiled into the bundle. Set the variable on the build machine and rebuild; removing the fallback makes the problem fail loudly next time.
- Do console.log statements affect performance?
- A few have negligible cost. The bigger issue is what they print: user objects, tokens and API responses logged in the browser are visible to anyone with DevTools open and to browser extensions. Strip them from production client builds.
- How do I check if my site is a production build?
- Look for dev-only markers: /@vite/client in the page source, a red React DevTools icon, React warnings in the console, or unminified JavaScript. Also check the start command your host runs; it should not be a dev command.