docs/
Outline
---
title: What to move into config first: a checklist
slug: config-checklist
lang: en
published: 2026-11-07
words: 427
lines: 67
size: 2.4 KB
read: 3 min read
---
What to move into config first: a checklist
The corner shop puts a new price sticker on every can. The big store keeps the price in one system, and every till reads it from there. In code, that system is config: a value is written in one place, and the rest of the code reads it from there.
This is the order I move values out of code, with real examples from the config of this site.
1. Secrets, before anything else
Passwords, API keys and signing keys never sit in code or in a YAML file. The YAML only names the environment variable, and startup fails when it is missing:
django:
secret_key: ${DJANGO_SECRET_KEY}
database:
url: ${DATABASE_URL}
2. Anything that changes between machines
Domains, URLs, hosts and the storage backend are different on your laptop and on the server. Give them a default only when the default is safe:
app:
site_url: ${SITE_URL}
storage:
backend: ${STORAGE_BACKEND:-local}
3. Limits and timeouts
Retries, timeouts, upload sizes and rate limits are the numbers you end up tuning. Put the unit in the key name, so nobody guesses whether 10 means seconds or minutes:
auth:
access_token_minutes: 15
login_failures_limit: 5
login_window_minutes: 15
uploads:
video:
max_mb_s3: 300
4. Values one file writes and another reads
If the same number appears in two places, one of them will be forgotten. That is the sticker the shop missed. Write it once and let both read it.
5. Product knobs
Cache windows, how many items a page shows, feature switches. Change them without touching logic:
api:
revalidate_seconds: 60
Three rules that keep config honest
- Validate it. My schema is strict: an unknown or missing key stops startup with its path in the message. A typo fails loudly instead of silently using a default.
- One loader. Only the config loader reads the environment. The rest of the code gets a typed config object.
- One base, small overrides.
base.yamlholds everything;production.yamloverrides only what differs, such as turning the API docs off.
What stays in code
Not every number is config. A value that never changes and only makes sense next to its logic, such as the number of days in a week, stays a named constant in code. Config is for the numbers you will change.
Check your repo
Search your code for numbers and strings that look like settings. Every hit is a sticker on a can. Move it to config, give it a clear name with its unit, and read it from one place.
| The corner shop puts a new price sticker on every can. The big store keeps the price in one system, and every till reads it from there. In code, that system is config: a value is written in one place, and the rest of the code reads it from there. |
| This is the order I move values out of code, with real examples from the config of this site. |
| ## 1. Secrets, before anything else |
| Passwords, API keys and signing keys never sit in code or in a YAML file. The YAML only names the environment variable, and startup fails when it is missing: |
| ```yaml |
| django: |
| secret_key: ${DJANGO_SECRET_KEY} |
| database: |
| url: ${DATABASE_URL} |
| ``` |
| ## 2. Anything that changes between machines |
| Domains, URLs, hosts and the storage backend are different on your laptop and on the server. Give them a default only when the default is safe: |
| ```yaml |
| app: |
| site_url: ${SITE_URL} |
| storage: |
| backend: ${STORAGE_BACKEND:-local} |
| ``` |
| ## 3. Limits and timeouts |
| Retries, timeouts, upload sizes and rate limits are the numbers you end up tuning. Put the unit in the key name, so nobody guesses whether `10` means seconds or minutes: |
| ```yaml |
| auth: |
| access_token_minutes: 15 |
| login_failures_limit: 5 |
| login_window_minutes: 15 |
| uploads: |
| video: |
| max_mb_s3: 300 |
| ``` |
| ## 4. Values one file writes and another reads |
| If the same number appears in two places, one of them will be forgotten. That is the sticker the shop missed. Write it once and let both read it. |
| ## 5. Product knobs |
| Cache windows, how many items a page shows, feature switches. Change them without touching logic: |
| ```yaml |
| api: |
| revalidate_seconds: 60 |
| ``` |
| ## Three rules that keep config honest |
| - **Validate it.** My schema is strict: an unknown or missing key stops startup with its path in the message. A typo fails loudly instead of silently using a default. |
| - **One loader.** Only the config loader reads the environment. The rest of the code gets a typed config object. |
| - **One base, small overrides.** `base.yaml` holds everything; `production.yaml` overrides only what differs, such as turning the API docs off. |
| ## What stays in code |
| Not every number is config. A value that never changes and only makes sense next to its logic, such as the number of days in a week, stays a named constant in code. Config is for the numbers you will change. |
| ## Check your repo |
| Search your code for numbers and strings that look like settings. Every hit is a sticker on a can. Move it to config, give it a clear name with its unit, and read it from one place. |




