The Anti-Config Manifesto
Runtime configuration creates a gap between source code and deployed behavior. Some runtime inputs are necessary. Every additional source makes behavior harder to reproduce and test.
The same binary, different behavior
An application's behavior may depend on startup arguments, configuration files, environment values, secret stores, remote controls, database values, and defaults in source.
Runtime behavior is the combination of code, active values, and the rules that decide which value wins. Reconstructing it requires every source and its history.
Development, testing, and production may run the same binary with different behavior. A production-only failure can be a configuration difference rather than a code difference. Diagnosis begins by finding values spread across several systems.
Source is version-controlled. Effective configuration often is not in one place.
How the second control plane grows
Configuration began with a sound premise: secrets and environment-specific values should not be hardcoded. Credentials and host details vary between deployments. Putting them in source is both a security risk and an operating obstacle.
The response can spread much further than necessary. Flags, limits, timeouts, retries, templates, and messages accumulate into a second control plane. Its parts may be reviewed individually while their combined state remains difficult to reproduce.
Feature flags make the trade visible. Changing behavior without deploying can reduce rollout risk. It can also bypass the normal relationship between a reviewed release and its behavior. Flags interact, outlive their purpose, and create a vendor dependency when their state lives in a hosted service.
Different values produce different behavior across environments. A typo, a reversed flag, or an incomplete secret rotation can change production without a code release. Configuration changes have caused major Meta and Cloudflare outages. Independent flags multiply possible states. Teams test only a fraction and assume the rest combine safely.
Behavior in source
Product behavior belongs in source. A behavior change is reviewed, tested, and deployed through the same path as the rest of the application.
Runtime inputs remain for facts the build should not know: where to connect, where data lives, and which credentials the process may use. Those inputs should be few, explicit, and validated at startup. Secrets are not product behavior. They belong in access-controlled facilities, not source control or casual process settings.
A fast, reliable deployment makes this discipline practical and leaves one release history to inspect.
When a flag is necessary, it needs an owner and a removal condition. Experiment definitions should remain visible in source. Emergency modes should be small, documented, and tested as emergency paths rather than used as routine product configuration. If deployment is too slow for ordinary behavior changes, improving deployment may be safer than building a parallel control plane around it.
The important distinction is not source versus environment variables. It is environment facts versus product policy.
Behavior belongs in code. Keep the exceptions explicit.