JSON as configuration
From package.json to tsconfig.json to cloud deployment manifests, JSON is everywhere as a configuration format. It is universal and tool-friendly — but it also has sharp edges (no comments, easy to typo) that bite teams who treat config casually. Here is how to do it well.
Structure for clarity
Group related settings into nested objects rather than a flat soup of keys:
{
"server": { "port": 8080, "host": "0.0.0.0" },
"database": { "url": "postgres://...", "poolSize": 10 },
"features": { "betaSearch": false, "newCheckout": true }
}Nesting communicates intent and makes it obvious which subsystem a setting belongs to.
Validate config with a JSON Schema
Config errors are deploy-breaking and often discovered too late. Define a JSON Schema and validate at startup so a typo fails fast with a clear message instead of a mysterious runtime crash:
{
"type": "object",
"properties": {
"server": {
"type": "object",
"properties": {
"port": { "type": "integer", "minimum": 1, "maximum": 65535 }
},
"required": ["port"]
}
},
"required": ["server"]
}A schema also powers editor autocomplete and inline error highlighting via the $schema key — many editors read it automatically.
The "no comments" problem
Standard JSON forbids comments, which is painful for config you want to annotate. Options:
- `$schema` and self-describing keys. Make keys clear enough that comments are unnecessary.
- JSONC / JSON5. Supersets that allow comments and trailing commas (
tsconfig.jsonis JSONC). Use only where your tooling supports them. - YAML or TOML. If you need rich comments, these formats are designed for it — convert to JSON at build time if your app expects JSON.
Never commit secrets
Configuration files live in version control; secrets must not. Keep credentials out of JSON config and load them from environment variables or a secrets manager. A safe pattern is a config file with placeholders and an .env for the real values:
{ "database": { "url": "${DATABASE_URL}" } }Environment-specific config
Avoid one giant file with every environment inline. Common approaches:
- A base config plus per-environment overrides merged at load time (
config.json+config.production.json). - Environment variables for anything that differs between deploys.
- A single schema validating all of them so every environment stays consistent.
Best practices checklist
- Nest related settings; avoid deep nesting beyond three or four levels.
- Validate against a JSON Schema at startup.
- Reference the schema with
$schemafor editor support. - Keep secrets in env vars, never in committed JSON.
- Use consistent key casing (pick
camelCaseorsnake_caseand stick to it). - Document non-obvious settings — in a README if the format forbids comments.