Need help with your JSON?
Try our JSON Formatter tool to automatically identify and fix syntax errors in your JSON. JSON Formatter tool
Continuous Validation of JSON Configuration Files
Continuous validation means your JSON configuration files are checked automatically in the editor, before commit, in CI, and again when the app starts. The goal is simple: a broken config should fail fast long before it becomes a production incident.
The most effective setup is layered. Parse the file to catch invalid JSON, validate it against a schema to catch missing or mistyped fields, then run a small set of semantic checks for rules your schema cannot express cleanly, such as cross-field dependencies or environment-specific constraints.
If humans edit these files directly, the biggest reliability gains usually come from two rules: make the schema strict and run the exact same validation command locally and in CI.
What to Validate Continuously
- Syntax: Catch malformed JSON immediately. A missing comma, trailing comma, or unescaped quote should never make it into a branch.
- Schema: Enforce required fields, correct types, enum values, and disallow stray keys that often come from typos.
- Semantic rules: Check things like allowed hostnames, file-path existence, mutually exclusive options, or feature-flag dependencies.
- Runtime loading: Validate on startup so environment-specific overrides and deployment-time mistakes still fail safely.
Choose Your JSON Schema Dialect Deliberately
JSON Schema draft 2020-12 is the current specification release, so it is the right choice when you need modern keywords such as prefixItems, unevaluatedProperties, or $dynamicRef.
But tooling compatibility still matters. VS Code's built-in JSON support fully covers older drafts more consistently and only has limited support for 2019-09 and 2020-12. Ajv also treats draft-07 as its default and requires a separate 2020 entry point if you adopt the newer draft. In practice, teams often choose:
- Draft 2020-12: Best when you need newer schema features and your validators are already standardized in CI.
- Draft-07: Best when broad editor compatibility and simpler validator setup matter more than the newest keywords.
The important part is consistency. Do not mix schema drafts casually across the same validation path or you will end up debugging the tooling instead of the config.
Start with a Strict Contract
Treat the configuration file as an API contract. Most weak validation setups fail because the schema is too permissive. A good baseline is to require the keys you actually need and set additionalProperties: false at every object boundary where stray keys should be rejected.
Example config:
{
"serviceName": "auth-api",
"port": 8080,
"logLevel": "info",
"featureFlags": {
"selfServiceSignup": true
},
"auth": {
"enabled": true,
"issuer": "https://login.example.com/",
"audience": "auth-api"
}
}Matching schema:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"additionalProperties": false,
"properties": {
"serviceName": { "type": "string", "minLength": 1 },
"port": { "type": "integer", "minimum": 1, "maximum": 65535 },
"logLevel": { "enum": ["debug", "info", "warn", "error"] },
"featureFlags": {
"type": "object",
"additionalProperties": { "type": "boolean" }
},
"auth": {
"type": "object",
"additionalProperties": false,
"properties": {
"enabled": { "type": "boolean" },
"issuer": { "type": "string", "format": "uri" },
"audience": { "type": "string", "minLength": 1 }
},
"required": ["enabled", "issuer"]
}
},
"required": ["serviceName", "port", "logLevel", "auth"]
}This already catches several common mistakes: unknown top-level keys, a string where a port number should be, a bad log level, or an invalid authentication issuer URL. If your team uses commented JSON or trailing commas, remember that plain JSON.parse() rejects them; use a JSONC-aware parser only if that format is intentional.
Run One Validation Command Everywhere
The cleanest continuous validation setup is a single command that developers can run locally and CI can run unchanged. Keep syntax, schema, and semantic checks behind one script so the result is reproducible.
Example package script:
{
"scripts": {
"validate:config": "ajv validate -s config/schema.json -d \"config/*.json\" --spec=draft2020 && node scripts/validate-config-semantics.mjs"
}
}- Editor: Associate your config files with the schema so developers see errors while typing.
- Pre-commit hook: Run the validation script before invalid config changes are committed.
- CI: Run the same command on every pull request and fail the build on any validation error.
- Release pipeline: Re-run validation for the environment-specific config bundle that will actually be deployed.
Keep a Runtime Guard
CI reduces risk, but it does not eliminate it. Runtime validation still matters when configs are assembled from secrets managers, environment overrides, mounted files, or generated deployment artifacts.
Runtime validation with Ajv:
import fs from "node:fs";
import Ajv2020 from "ajv/dist/2020";
import addFormats from "ajv-formats";
import schema from "./config.schema.json";
const ajv = new Ajv2020({ allErrors: true, strict: true });
addFormats(ajv);
const validate = ajv.compile(schema);
export function loadConfig(configPath: string) {
const text = fs.readFileSync(configPath, "utf8");
const config = JSON.parse(text);
if (!validate(config)) {
throw new Error(ajv.errorsText(validate.errors, { separator: "\n" }));
}
if (config.auth.enabled && !config.auth.audience) {
throw new Error("auth.audience is required when auth.enabled is true");
}
return config;
}This pattern gives you four separate protections: JSON syntax parsing, schema validation, readable error output, and a final semantic check for business rules that do not belong in the schema.
Practical Rules That Prevent Most Failures
- Reject unknown keys with
additionalProperties: falseunless the object is intentionally extensible. - Keep the schema next to the config or loader code so changes happen together in the same review.
- Validate example config files in the repository, not just production overrides.
- Fail on startup with a clear message instead of continuing with a partially valid configuration.
- Use semantic checks for rules involving the filesystem, network destinations, secret names, or cross-field dependencies.
Common Mistakes
- Only validating syntax: A file can be valid JSON and still be unusable by the application.
- Only validating in CI: Developers get slower feedback and runtime-only overrides can still break production.
- Using a loose schema: Optional-everything schemas create false confidence.
- Ignoring draft compatibility: Newer schema keywords are useful, but only when your editor and validator stack support them consistently.
Recommended Baseline
For most teams, the baseline that delivers the best return is straightforward: define a strict schema, wire it into editor feedback, run one validation command in pre-commit and CI, and validate again on application startup. That is the practical meaning of continuous validation for JSON configuration files.
If you need the newest JSON Schema features, adopt 2020-12 intentionally and standardize the validator path. If you mainly want broad tooling support and low friction, draft-07 is still a reasonable operational choice. Either way, consistent enforcement matters more than theoretical schema power.
Need help with your JSON?
Try our JSON Formatter tool to automatically identify and fix syntax errors in your JSON. JSON Formatter tool