systems nominaltopics live: 28drip-feed active

Config vs Code

配置与代码分离

01 What this concept is really about

Config vs code is about separating the values that change between environments from the logic that stays the same. Your application code should describe what the system does, while configuration describes how it behaves in a particular place. When you hardcode a database address, a port number, or a feature flag directly into your source files, you tie your code to one specific environment. That makes it painful to run the same code on a laptop, in a test environment, and in production without editing and rebuilding.

The core idea is that configuration is data, not logic. It lives outside your compiled artifacts and can be changed without rewriting the program. This separation lets you promote the same build through multiple stages, which is a foundation of reliable delivery. It also keeps sensitive values like passwords and API keys out of your codebase, where they are hard to rotate and easy to leak. In practice, you move these values into environment variables, configuration files, or a dedicated configuration service, and your code reads them at startup or runtime.

This concept is not about any single tool or platform. It is a design principle: keep the things that vary separate from the things that stay the same. Once you adopt that mindset, you start to see hardcoded values everywhere, and you gain a clear path to making your systems more portable, testable, and secure.

02 Why it matters for your systems

When configuration is mixed into code, every environment change becomes a code change. That means a new commit, a new build, and a new deployment just to change a timeout or a log level. This slows down feedback and increases the chance of introducing bugs. Separating config lets you adjust behavior without touching the code, so you can respond to operational needs quickly and safely.

It also improves security and compliance. Secrets such as database passwords, tokens, and certificates should never be committed to version control. By moving them to environment variables or a secrets manager, you reduce the blast radius if your repository is exposed. You can rotate credentials without redeploying, and you can grant different access levels to different environments. This is a basic expectation in modern DevOps practices.

Finally, separation makes your systems easier to test and reason about. You can run the same application with a test configuration that points to a local database, and a production configuration that points to a managed service. Your automated tests become more reliable because they control the configuration explicitly. In short, separating config from code is a small change in habit that pays off in speed, safety, and clarity across the entire lifecycle of your software.

03 The mistake people often make

The most common mistake is hardcoding values directly in source files. This often starts innocently: a developer writes a connection string or an API endpoint right where it is used, because it is quick and works locally. Over time, these values spread across the codebase, and changing an environment means hunting through many files. Worse, secrets get committed to version control, where they remain in history even after removal.

Another mistake is treating configuration as an afterthought. Teams may create a single configuration file that is copied and edited manually for each environment. This leads to drift, where environments slowly diverge and nobody knows which settings are correct. Manual edits are error-prone and hard to audit. A related error is using conditional logic in code to handle environment differences, such as if (environment == "production") branches. This couples code to specific environments and makes the application harder to understand.

A third mistake is confusing configuration with feature flags or business rules that change frequently. Not everything that varies should be an environment variable. Some values belong in a database or a dedicated feature management system. The key is to recognize what truly differs per environment versus what is part of the application's domain logic. Avoiding hardcoded values does not mean externalizing everything; it means being deliberate about what you separate and why.

04 A simple way to learn it

A practical way to learn this concept is to take a small application you already have and perform a configuration audit. Start by searching your codebase for literal strings and numbers that look like environment-specific values: URLs, hostnames, ports, credentials, file paths, and timeouts. Make a list of each one and note where it is used. This exercise alone will show you how much is currently hardcoded.

Next, choose one value from your list and move it to an environment variable. Update your code to read that variable at startup, with a sensible default for local development. Run your application locally to confirm it still works. Then create a second configuration, perhaps for a test environment, and verify that the same code can run with different values without any code changes. Repeat this process for a few more values, grouping related settings into a single configuration file or a structured format like JSON or YAML.

Finally, document your configuration. Create a sample configuration file that lists every required variable, with placeholder values and comments explaining what each one does. This becomes a contract for anyone running your application. By repeating this cycle—audit, extract, test, document—you build the habit of separating config from code. You do not need a complex tool to start; environment variables and a well-documented sample file are enough to learn the principle.

05 What to pair it with

Separating config works best when paired with environment variables. Environment variables are a simple, universal way to inject configuration at runtime without changing code. They are supported by nearly every operating system and deployment platform, and they keep secrets out of your source tree. Use them for values that differ between environments, such as database URLs, API keys, and feature toggles.

Another natural pairing is a secrets manager. While environment variables are convenient, they can be exposed in process listings or logs. A dedicated secrets manager provides encryption, access control, and audit trails. Your code reads secrets from the manager at runtime, often through a library or sidecar. This adds a layer of security without complicating your application logic.

Infrastructure as code is a third powerful companion. When you define your infrastructure in code, you can also define the configuration that goes with it. This ensures that environments are created consistently and that configuration is versioned alongside the infrastructure. Together, these practices form a cohesive approach: code defines behavior, environment variables and secrets provide runtime values, and infrastructure as code manages the environment itself. This combination reduces manual steps and makes your systems more reproducible and secure.

06 When to keep it simple

Not every project needs a complex configuration system. For a small script or a personal project, a single configuration file with a few values may be perfectly adequate. The goal is to avoid hardcoding values that you will need to change, not to build an enterprise-grade configuration service. If you are the only person running the code and it runs in one place, a simple file is fine.

Similarly, if a value truly never changes across environments, it may not need to be externalized. For example, the number of seconds in a minute is a constant, not configuration. The key question is: will this value differ between environments or over time? If the answer is no, keeping it in code is reasonable. Over-engineering configuration can make a small project harder to understand and maintain.

When you do need to keep it simple, choose the lightest approach that solves the problem. Environment variables are often enough. Avoid introducing a configuration server or a complex hierarchy until you feel real pain from the simple approach. Let the complexity of your configuration grow only as your system grows. This incremental approach keeps you focused on delivering value while still following the principle of separating config from code.

07 Questions people ask

Q: What is the difference between configuration and code? A: Code defines the logic and behavior of your application—what it does. Configuration defines the values that control how that logic runs in a specific environment, such as addresses, credentials, and feature flags. Code is typically built and versioned as an artifact; configuration is supplied at deployment or runtime and can change without rebuilding.

Q: Should I ever hardcode values? A: Yes, for true constants that never change and are part of the application's logic, such as mathematical constants or fixed protocol strings. Hardcoding is a problem when the value is environment-specific or sensitive. If you find yourself editing code to change a value for a different environment, that value should be configuration.

Q: How do I handle secrets in configuration? A: Never store secrets in your code repository. Use environment variables for simple cases, but for stronger security, use a dedicated secrets manager that encrypts values and controls access. Inject secrets at runtime, and rotate them regularly. This keeps them out of logs and version history, reducing the risk of exposure.