The Environment Variable Patterns That Silently Break Production Apps
Missing environment variables crash apps loudly. Wrong ones cause failures that are much harder to diagnose. Here are the patterns that show up most often and how to catch them before your users do.
Missing environment variables are easy to catch. Your app crashes on startup with a clear error: cannot read property of undefined, connection refused, invalid API key. You see it in the logs immediately.
Wrong environment variables are harder. The app starts. It looks like it is working. Users can log in. Things respond. And then something breaks in a way that takes you an hour to trace back to a value that was set incorrectly six weeks ago.
Here are the patterns that cause these failures and how they typically manifest.
The development value that made it to production
This is the most common pattern. A value that was correct for development gets copied to the production environment without being updated.
The most damaging version of this is a test API key in production. Stripe test keys look like real Stripe keys. The app starts fine. Users complete checkout. The payment form accepts the card. No error is thrown. The money never actually moves. You find out when a user emails asking why they were charged but their account was never upgraded.
The same pattern applies to database URLs pointing to a development database, webhook endpoints pointing to localhost, email providers in sandbox mode that accept but never deliver, and SMS providers with test credentials that silently drop messages.
The fix is to treat every environment variable as environment-specific. There should be no variable that is valid in both development and production without being deliberately reviewed.
The variable that exists but is empty
Many deployment platforms let you create environment variables with no value. The variable key exists, the value is an empty string. Your app reads it, gets an empty string, and does something it should not.
Depending on how the variable is used, an empty string can cause a connection to fail, a feature to be permanently disabled, a redirect to go to the root of the app, or an API call to authenticate with no credentials and get a confusing 401 response.
The correct behavior is usually to check for the presence and validity of a value, not just its existence. if (process.env.SOME_KEY) evaluates to false for an empty string. if (process.env.SOME_KEY !== undefined) evaluates to true. These are not the same check.
The boolean that is actually a string
Environment variables are always strings. When you set FEATURE_ENABLED=false in your deployment platform, the value your app reads is the string "false", not the boolean false. In JavaScript, the string "false" is truthy. A check like if (process.env.FEATURE_ENABLED) will evaluate to true even when you intended it to be false.
This causes features to stay enabled when you thought you disabled them, or disabled when you thought you enabled them.
The correct pattern is to compare explicitly: process.env.FEATURE_ENABLED === 'true'.
The secret with whitespace
When copying secrets from a dashboard into a deployment platform, it is easy to accidentally include a leading or trailing space. The value looks correct when you view it. It pastes correctly. But the actual value your app reads has a space at the start or end that invalidates the signature, breaks the HMAC, or causes the API call to reject the key.
This is particularly common with long secrets like JWT signing keys or webhook secrets that you copy from a provider dashboard. Most providers do not show whitespace visually in the dashboard. You cannot tell by looking.
The symptom is intermittent or consistent authentication failures where the key appears to be set correctly.
The variable that changes between restarts
Some apps read environment variables at startup and cache the values. Others read them on every request. Hosting platforms that manage environment variables may apply changes at different times depending on whether they restart the container.
The failure mode is a value that was updated in the platform but the app is still reading the old value, or the opposite: the app reads a new value for some requests and an old value for others if multiple instances are running and not all of them have restarted.
The safe approach is to restart all running instances after changing any environment variable that the app reads.
The URL that is missing a trailing slash (or has one when it should not)
APIs and services are inconsistent about trailing slashes. Some require them. Some reject them. When you concatenate a base URL from an environment variable with a path, the result changes depending on whether the base URL ends with a slash.
https://api.example.com + /users = https://api.example.com/users
https://api.example.com/ + /users = https://api.example.com//users
The double slash is usually handled gracefully, but some servers reject it, some proxies strip it, and some rate limiters treat it as a different endpoint. The failure is intermittent and path-dependent, which makes it hard to trace.
The variable your app needs that you did not know about
This is the silent failure that is hardest to catch before launch. Your app references an environment variable somewhere, deep in a library, in a utility function that only runs in certain conditions, in a middleware that only activates on certain routes, and you do not know it exists until that code path runs.
The app starts. Most things work. A user hits a specific page or triggers a specific action and something fails. You dig into the logs, find an undefined reference to an environment variable you never set, and have to update the configuration and redeploy.
The only reliable way to catch this before users do is to read every environment variable reference in your codebase and cross-reference it against what you have set. That is a tedious manual process.
Jetpacked does this automatically. When you connect a repository, it reads every process.env reference in your code, every env() call in your config files, every secret reference in your ORM configuration, and produces a complete list. Anything not set in your environment gets flagged before deployment.
The practical checklist
Before any deployment, verify the following:
Every environment variable referenced in your code is set in your deployment platform, with a real value, not an empty string.
Every value that differs between development and production has been explicitly reviewed and updated.
Every boolean flag is compared explicitly as a string, not used in a truthy check.
API keys, tokens, and secrets were copied cleanly without leading or trailing whitespace.
Any variable that changed recently has been followed by a restart of all running instances.
Environment variable failures are almost always fixable within minutes once you know what is wrong. The problem is that they can be hard to diagnose because the error messages rarely point directly at the variable. Getting the configuration right before launch is significantly easier than debugging it with users waiting.
Deploy your app in minutes
Jetpacked handles Docker, HTTPS, databases, and deployments so you can focus on building.
Launch your app free