Environment and configuration
Configuration should fail clearly, keep secrets on the server, and make the differences between local, preview, and production environments deliberate.
Treat NEXT_PUBLIC values as published values
A variable prefixed with NEXT_PUBLIC can be included in browser JavaScript. Anybody can inspect it. Use that prefix for values that are meant to be public, such as an analytics site ID, never for a database password or a private API token.
Unprefixed variables are not automatically safe if you pass them into a Client Component or return them from an endpoint. Secrets stay private because your server code chooses not to expose them.
Fail at startup, not during the first real request
Reading process.env throughout the codebase spreads uncertainty. Create one server-only configuration module, validate required values, and export a typed object. A missing database URL should stop the build or boot process with a useful message.
This also gives tests a single place to provide controlled configuration and prevents small spelling differences from becoming runtime bugs.
import 'server-only'
function required(name: string) {
const value = process.env[name]
if (!value) throw new Error(`Missing environment variable: ${name}`)
return value
}
export const env = {
databaseUrl: required('DATABASE_URL'),
authSecret: required('AUTH_SECRET'),
appUrl: required('APP_URL'),
}Know whether a value is needed at build time or runtime
A statically generated page may read configuration while the application is building. A Route Handler may read it when a request arrives. That difference matters when one image is promoted through several environments or when secrets are injected only at runtime.
Document when each required value is consumed. If one build artifact must run in several environments, avoid baking environment-specific public values into that artifact unless that is intentional.
Preview should resemble production where it matters
Preview deployments are most useful when they exercise the same authentication flow, database shape, headers, and external integrations as production without sharing production data. A preview that silently disables half the system can only review appearance.
Use separate credentials and isolated data, but keep the integration boundaries realistic. Record intentional differences so a developer does not spend an afternoon debugging behaviour that was disabled by configuration.
Plan for secret rotation
Secrets eventually expire, leak, or need replacement. Know which deployment or service owns each secret and whether rotating it interrupts active sessions or callbacks.
When a provider supports overlapping keys, accept the old and new value during a short migration window. Rotate, verify traffic on the new key, then remove the old one.
Can you explain “Environment and configuration” without reaching for the API reference?
Describe the main trade-off, one failure mode, and the decision you would make in a real project.