Quick answer: Environment config belongs outside the source because it changes between development, staging, and production while the code stays the same. When an agent writes a URL, bucket name, or connection string as a literal to make the current run pass, it bakes one environment's values into every deploy. Read environment-specific values from a single config layer, validate them at startup, and keep secrets out of the repository.
Why agents reach for literals
A literal is the shortest path to a green run. The agent needs a value to proceed, the environment is not defined, and typing it inline removes the friction immediately. Training data is full of examples with hardcoded endpoints, and unless the task explicitly asks for externalized config, the agent optimizes for working code on this machine rather than portable code on the next.
The failure is silent at first. It works on the agent's machine because that literal happens to match the local environment. It breaks when the same code points at a development database from production, when a host name changes and requires a full rebuild, or when a committed credential has to be rotated by editing source.
What actually counts as environment config
Not every literal is a problem. Values that never change between environments, like a fixed page size or a well-known constant, can stay in code. The dividing line is whether the value differs by environment or deployment. For how this fits the broader work of running coding agents well, see the AI coding agent guide.
| Value type | Where it belongs | Example |
|---|---|---|
| Endpoint, host, port | Environment variable, loaded at startup | API base URL, database host |
| Resource names that vary by stage | Environment config | Bucket, queue, topic names |
| Secrets and credentials | Secret manager, never in source | API keys, passwords |
| Stable domain constants | Source is fine | Fixed limits, math constants |
How to move config out of code cleanly
Use one loading point. Read the environment once, in a single module, and expose typed values to the rest of the app instead of reading process variables ad hoc across the codebase. That gives you one place to audit and one place to fail.
Provide safe defaults for non-sensitive values and let the environment override them, but never guess a missing required value. Validate that every required variable is present and well formed at startup, and fail fast with a clear message before the app handles traffic. Keep secrets in a secret manager rather than a committed .env file; a local .env, listed in .gitignore, works for development as long as the required variables are documented.
Centralizing reads also makes the configuration discoverable. When every variable enters through one module, you can list the full set of environment dependencies in a single place and confirm nothing reads a variable the startup check does not know about. Scattered reads hide unused and missing variables, and they make a secret harder to locate because it could be referenced anywhere in the codebase.
Keep defaults honest. A default works for a non-critical value with a safe fallback, but it should never point at a real resource. If a missing production variable silently falls back to a development endpoint, the problem stays hidden until the wrong data is written. Required values should raise an error rather than inherit a convenient default.
For the boundary with a related failure, committing a machine-specific absolute path is a separate portability problem; here the issue is values that legitimately change between environments. Powerduck keeps environment-specific values in scoped variables rather than literals, with globals, collection, environment, and local scopes, and when no base URL is available it asks for one instead of guessing, which is the same fail-fast rule applied to request configuration.
Frequently asked questions
Is a .env file enough? For local development, yes, as long as it is not committed and secrets are excluded. In production, prefer a managed secret store and inject values through the platform.
Should I externalize every constant? No. Move only values that change across environments or deployments. Externalizing stable constants adds indirection with no benefit.
What if the agent keeps hardcoding values? Make externalization part of the task and the review. Require the config layer up front and reject any diff that introduces an environment-specific literal.
The bottom line
Code is built once and deployed many times; environment config is the part that changes between those deployments. Keep the two separate, load config through one validated entry point, fail loudly when a required value is missing, and never commit a secret. A run that passes on a hardcoded value is only passing for one environment.
Comments
Join the discussionNo comments yet. Be the first to comment.

