Why Configuration File Rules Matter
Configuration files are deceptively simple — they're 'just data.' But AI assistants make three critical mistakes with config files: embedding secrets in plaintext, hardcoding environment-specific values instead of using variables, and creating inconsistent structures across config files in the same project. Each mistake creates either a security vulnerability, a deployment problem, or a maintenance headache.
The challenge is that config files span many formats (JSON, YAML, TOML, INI, .env) and many purposes (application config, CI/CD, infrastructure, editor settings). Your rules need to cover the patterns that apply across all formats while being specific enough to prevent the common AI mistakes.
These rules apply to any configuration file the AI generates — package.json, docker-compose.yml, tsconfig.json, .env, GitHub Actions workflows, and application config files.
Rule 1: Never Secrets in Config Files
The rule: 'Never put secrets (API keys, passwords, tokens, connection strings, private keys) in any configuration file that is committed to version control. This includes JSON, YAML, TOML, and any other config format. Use environment variables for secrets at runtime. Use .env files for local development (always in .gitignore). Use your platform's secret management for CI/CD and production.'
For .env files: '.env files are for local development only. They must be in .gitignore — never committed. Include a .env.example file with placeholder values and descriptions for every required variable. Never put real secrets in .env.example — use obviously fake values: API_KEY=your-api-key-here.'
AI assistants put real-looking secrets in config files because it makes the example work immediately. One rule prevents every secret from entering git history — which is permanent even if you delete the file later.
- No secrets in committed files — JSON, YAML, TOML, INI, any format
- .env for local dev only — always in .gitignore
- .env.example with placeholder values — committed as documentation
- CI/CD: GitHub Secrets, Vercel env vars, AWS Secrets Manager
- Production: secrets manager or encrypted environment variables
Even if you delete a file with a secret, it's in git history forever. The only fix is rotating the secret. One rule — no secrets in committed files — prevents every secret leak.
Rule 2: Environment-Specific Configuration
The rule: 'Separate configuration into three layers: defaults (committed, same across environments), environment overrides (per-environment, may be committed if non-secret), and secrets (never committed, injected at runtime). Never hardcode production URLs, database hosts, or service endpoints in config files that are committed to the repo.'
For application config: 'Use a configuration hierarchy: config/default.json for shared defaults, config/development.json for dev overrides, config/production.json for prod overrides. Load with a config library that merges layers: defaults → environment → environment variables. Environment variables always override file-based config.'
For Docker Compose: 'Use docker-compose.yml for the base configuration, docker-compose.override.yml for local dev overrides (auto-loaded by Docker Compose), and docker-compose.prod.yml for production overrides (loaded explicitly with -f). Never put dev-specific config (volume mounts, debug ports) in the base file.'
Rule 3: Schema Validation for Config Files
The rule: 'Define JSON Schema for all application configuration files. Validate config on application startup — fail fast on invalid config with a clear error message. Use TypeScript types inferred from the schema (with zod or json-schema-to-typescript) to get type safety in config access. Never access config values without validation.'
For YAML: 'Use YAML schema validation for CI/CD configs (GitHub Actions, GitLab CI). Most CI platforms validate workflow syntax — but application-level YAML configs need explicit schema validation. Use ajv or similar validators for runtime validation.'
For editor support: 'Reference JSON Schema in config files with the $schema property for editor autocompletion and inline validation. VS Code and JetBrains IDEs provide real-time feedback when a schema is referenced. Example: { "$schema": "https://json-schema.org/draft/2020-12/schema" }.'
Validate config on application startup with JSON Schema. A clear error at boot ('missing DATABASE_URL') is infinitely better than a cryptic crash at runtime when the first query runs.
Rule 4: Consistent Structure Conventions
The rule: 'All config files in the project follow the same conventions. JSON: 2-space indent, no trailing commas, sorted keys in package.json. YAML: 2-space indent, no tabs, use block style (not flow style), anchor/alias only when genuinely reusable. Comments in YAML explain why a value is set, not what it is.'
For naming: 'Config file names use lowercase with dots or hyphens: app.config.json, docker-compose.yml, tsconfig.json. Environment-specific files include the environment: config.production.json, .env.staging. Never use spaces or uppercase in config file names.'
For organization: 'Keep config files in the project root (package.json, tsconfig.json) or in a config/ directory (application-specific config). Don't scatter config files across nested directories. Use a single source for each config concern — never split database config across three files.'
- JSON: 2-space indent, sorted keys in package.json, no trailing commas
- YAML: 2-space indent, block style, comments for rationale
- Lowercase filenames: app.config.json, docker-compose.yml
- Root or config/ directory — don't scatter across nested paths
- One source per concern — don't split database config across files
Rule 5: Rules for Specific Config Files
For package.json: 'Sort dependencies alphabetically. Use exact versions (1.2.3) or caret (^1.2.3), never tilde (~1.2.3) unless you have a specific reason. Include engines field with minimum Node version. Scripts should be named consistently: dev, build, start, test, lint, typecheck.'
For tsconfig.json: 'Use strict: true as the baseline. Enable noImplicitAny, strictNullChecks, noUncheckedIndexedAccess. Set target and module to match your runtime (ESNext for modern Node, ES2022 for broader compatibility). Use paths for import aliases: {"@/*": ["./src/*"]}.'
For .gitignore: 'Include: node_modules/, dist/, build/, .env, .env.local, .env.*.local, *.log, .DS_Store, coverage/, .turbo/, .next/, .vercel/. Use comments to explain non-obvious entries. Never gitignore config files that should be committed (tsconfig, eslintrc, prettierrc).'
strict: true in tsconfig.json enables noImplicitAny, strictNullChecks, and more. It's the single most impactful TypeScript config setting — catches bugs at compile time that would be runtime errors.
Complete Configuration Rules Template
Consolidated rules for configuration files across all formats.
- No secrets in committed files — .env for local dev (in .gitignore), secrets manager for prod
- .env.example with placeholders — committed as documentation for required variables
- Three-layer config: defaults (committed) → env overrides → env vars (highest priority)
- JSON Schema validation on startup — fail fast with clear error messages
- 2-space indent for JSON and YAML — sorted keys in package.json
- Lowercase filenames — root or config/ directory — one source per concern
- tsconfig.json: strict: true, noUncheckedIndexedAccess, path aliases
- package.json: exact or caret versions, engines field, consistent script names