Quick answer: Reliable JSON configuration needs more than valid syntax. Use the parser your runtime actually supports, keep secrets outside committed files, minimize environment-specific copies, preserve value types and units, define merge precedence, validate the final effective configuration, and fail safely. Test the same startup path that production uses before routing traffic.
A configuration change can ship without touching application code and still alter databases, timeouts, feature flags, or external endpoints. That makes config review operational work, not a formatting chore. These seven mistakes explain why a file that looks harmless in a pull request can behave differently after deployment.
Inspect a sample configuration in the JSON Viewer and use JSON Compare to review the values that changed between versions.
If your application saves configuration at runtime, apply the six JSON file write checks as well. Correct settings can still disappear when concurrent writers replace snapshots computed from the same old file.
1. Are you writing JSONC or JSON5 for a strict JSON parser?
Standard JSON rejects comments, trailing commas, single-quoted strings, and unquoted property names. JSONC and JSON5 deliberately accept some of those features, but only when the consuming tool uses the matching parser. Confirm the exact parser used in production instead of assuming that editor highlighting or a successful local tool proves compatibility.
This file is invalid JSON even though several editors display it comfortably:
{
// Retry failed requests twice
"retries": 2,
}
A strict JSON.parse() call rejects both the comment and trailing comma. A JSONC-aware tool may accept both, while JSON5 accepts a wider set of JavaScript-like syntax. Renaming a file does not change the parser; the application must explicitly support that format.
The Google JSON Style Guide says comments should not be included in JSON objects. For the normative grammar, RFC 8259 defines JSON values and permitted whitespace without a comment production.
When a tool documents JSONC, name the file clearly and keep that parser in the execution path. If another service consumes the same artifact, convert it to strict JSON during the build and validate the generated output. Never remove comments with a regular expression; URLs and strings can contain the same slash sequences.
Our comments in JSON guide compares documented alternatives. Use JSON Prettifier for strict JSON syntax, not as proof that a JSONC-specific configuration will behave correctly in its target tool.
2. Are secrets committed inside the JSON configuration?
A valid JSON file is not a secure secret store. Configuration copied into Git, build artifacts, container layers, logs, tickets, or backups can persist long after one visible file is deleted. Keep credentials outside ordinary committed JSON, resolve them through an approved deployment mechanism, and rotate any secret that entered version history.
Avoid committing literal credentials:
{
"database": {
"host": "db.internal",
"password": "real-production-password"
}
}
A committed file can instead hold non-sensitive structure or a reference understood by the loader:
{
"database": {
"host": "db.internal",
"passwordSecret": "production/orders/database-password"
}
}
That reference is not a universal standard. Your platform must resolve it securely, authenticate to the secret store, limit access, and avoid printing the result. Document the boundary so nobody later treats the reference as a literal password.
Microsoft's current .NET application-secrets guidance warns against storing sensitive values in source code or configuration. It also notes that environment variables are commonly plain text and can be exposed if a machine or process is compromised. They are a delivery mechanism, not automatic encryption.
Add secret scanning before merge and review generated deployment manifests, not only source files. Our JSON redaction guide covers safer diagnostic copies, while the JSON security checklist addresses the wider trust boundary.
3. Do you maintain complete JSON copies for every environment?
Full configuration copies for development, staging, and production drift because most values are repeated but edited independently. Keep a small, non-secret base plus minimal environment overrides when your platform supports layering. Define ownership and precedence, and generate every environment's effective configuration in tests so a missing update cannot hide in an untouched copy.
Three 200-line files that differ in six places create a review problem. A security setting can change in the intended base but stay stale in one environment. Copying production config into staging also encourages credentials and endpoints to cross boundaries.
A more reviewable layout is:
config/
base.json
development.json
staging.json
production.json
config.schema.json
Small override files should contain deliberate differences. That design still needs an explicit merge rule. Does an absent property inherit? Does null erase a value? Do arrays replace, concatenate, or merge by index? A generic deep-merge library can choose behavior appropriate for user-interface state but dangerous for allowlists or ordered middleware.
The Twelve-Factor App configuration principle recommends separating deploy-varying configuration from code and keeping environment controls independently manageable. Its environment-variable approach is one model, not a requirement that every setting become an untyped string. Structured non-secret defaults can remain in JSON when precedence and typing are clear.
Use one loader module instead of scattered reads. It should assemble sources once, expose a typed effective configuration, and record which non-secret source won for each setting. Never log the full merged object.
4. Are strings, numbers, booleans, and units unambiguous?
JSON preserves basic value types, but deployment overrides often arrive as strings. The text "false" is truthy in many programming languages, and "30" does not reveal whether a timeout means milliseconds or seconds. Define each setting's type, range, enum, and unit, then reject ambiguous or out-of-range values before startup completes.
These documents are both valid JSON, but they do not express the same contract:
{
"featureEnabled": false,
"requestTimeoutMs": 30000,
"retryCount": 3
}
{
"featureEnabled": "false",
"requestTimeoutMs": "30",
"retryCount": -1
}
A permissive coercion layer can make the second appear to work until another runtime, validator, or deployment system interprets it differently.
Use names such as timeoutMs, cacheTtlSeconds, and maxUploadBytes when numeric units matter. Put realistic minimum and maximum values in a JSON Schema. Enumerate modes rather than accepting arbitrary strings. Reject unknown keys when typos are dangerous, while keeping documented extension points where forward compatibility matters.
Our JSON Schema guide covers types and constraints. The guide to eight schema validation mistakes explains why required, format, and default can surprise developers. Test the loader too; a correct schema cannot prevent later code from coercing values.
5. Do you validate source files but skip the effective configuration?
Validating base.json and production.json separately does not prove their merged result is usable. An override can replace a nested object, change a value's type, or leave a secret reference unresolved. Assemble configuration exactly as production does, then validate that effective object before any server, queue worker, or scheduled job begins work.
Suppose the base file contains:
{
"http": {
"port": 8080,
"timeoutMs": 5000
}
}
The production override contains:
{
"http": {
"timeoutMs": 15000
}
}
A shallow merge replaces the entire http object and loses port; a recursive merge preserves it. Neither behavior is universal. The loader's documented contract decides. The mistake is checking two valid fragments without inspecting what the application receives.
A practical preflight sequence is:
- Parse each source with its real parser.
- Apply sources in documented precedence order.
- Resolve approved secret references without logging values.
- Validate the effective object against the intended schema draft.
- Run semantic checks the schema cannot express cleanly.
- Print a redacted summary and exit before starting work when validation fails.
The official JSON Schema getting-started guide demonstrates object constraints. Store passing and failing fixtures beside the schema, including one fixture per environment. Syntax, structure, and operational reachability remain separate checks.
6. Does the application silently continue with unsafe fallback values?
A missing or malformed critical setting should not silently activate a broad permission, public bind address, disabled TLS check, or unlimited timeout. Classify settings by risk, choose explicit safe defaults only where they are genuinely safe, and stop startup with a redacted error when the application cannot establish a trustworthy effective configuration.
Defaults help local development, but they become dangerous when they hide deployment mistakes. Consider ALLOWED_ORIGINS. An empty value might mean deny all, allow all, or use a built-in list. If the loader guesses, a typo in an environment variable can silently change a security boundary.
Write absence behavior into tests:
| Setting type | Safer absence behavior | Example |
|---|---|---|
| Security boundary | Fail startup or deny by default | Allowed origins |
| Required dependency | Fail startup | Database endpoint |
| Performance tuning | Use bounded documented default | Pool size |
| Optional feature | Disable explicitly | Experimental exporter |
| Secret | Fail without revealing its value | Signing key |
An error can identify the setting path, expected source, and remediation without printing a credential. Exit with a nonzero status so the deployment system marks the instance unhealthy.
For broad background, Wikipedia's configuration-file overview describes the pattern across platforms. It is not the authority for your application's fallback rules. Those rules belong in your schema, loader tests, runbook, and deployment policy.
7. Can you prove the new configuration works and can be rolled back?
A syntactically valid, schema-valid configuration can still point to an unreachable service, incompatible region, expired certificate, or wrong tenant. Test high-impact settings through a dry run or narrow deployment, observe startup and dependency health, retain the last known-good version, and make rollback independent of the broken configuration path.
Begin with checks that do not perform destructive work: resolve DNS, establish authenticated connections, verify required topics or buckets exist, and confirm that the application can read its non-secret identity. Apply timeouts so a bad endpoint cannot stall deployment indefinitely.
Version config changes with code when they must ship together. If configuration is delivered separately, record its version or digest in deployment metadata. During an incident, an operator must be able to answer: which effective configuration is this instance using?
A safe rollout sequence is:
- Build and validate every environment's effective configuration.
- Scan committed and generated artifacts for secrets.
- Run the application's preflight command.
- Deploy to a limited target.
- Check health, dependency errors, and redacted source diagnostics.
- Expand gradually when the signals remain healthy.
- Restore the known-good version if acceptance checks fail.
Do not claim success because the process started. A worker can boot without permission to its queue, and an API can report healthy while its payment endpoint targets a test account. Define acceptance checks around the behavior controlled by the changed setting.
These seven mistakes form one review path: parse the intended format, separate secrets, minimize duplication, preserve types and units, validate the effective result, fail safely, and prove the rollout. A short JSON file deserves the same evidence as a code change when it controls production behavior.
If the source configuration is YAML, add the YAML-to-JSON conversion checks before validating the effective JSON contract. A successful conversion can still change types, root shape, or values that the application depends on.