Production Readiness Gates

Default contract rules that catch demo data, placeholder domains, and hardcoded IDs before they reach production.


Table of contents

  1. Why Production Readiness Gates?
  2. Rules
    1. PROD-001: No Demo or Mock Data in Production Code
    2. PROD-002: Domain Allowlist Enforcement
    3. PROD-003: No Hardcoded IDs in Production Code
  3. Quick Reference
  4. Installation
  5. Compliance Checklist
  6. Extending the Defaults
  7. Overriding Rules
  8. Relationship to Other Gates
  9. Related Pages

Why Production Readiness Gates?

Demo accounts, localhost URLs, and hardcoded UUIDs are the most common sources of production incidents in early-stage projects. These patterns are easy to introduce during development and easy to miss during review.

The production readiness contract (production_readiness_defaults.yml) catches these patterns with regex-based rules that run as part of your contract test suite. Like the security and accessibility gates, these are ready-to-use defaults that can be extended for your project.


Rules

PROD-001: No Demo or Mock Data in Production Code

Source code must not contain demo users, mock constants, or fake data references.

Forbidden patterns:

Pattern Message
/DEMO_USER/ Demo user reference – remove or gate behind feature flag
/DEMO_PLAN/ Demo plan reference – remove or gate behind feature flag
/MOCK_[A-Z_]{3,}/ Mock constant – use real values or environment variables
/['"]demo[@.].*['"]/i Demo email or domain – use environment-configured values
/['"]test_user['"]/i Test user reference – remove or gate behind feature flag
/fake(User\|Data\|Account\|Email\|Name)/i Fake data reference – use real values
/seed(User\|Data\|Account)s?\s*=/i Seed data assignment – move to seed scripts

Scope: src/**/*.{ts,js,tsx,jsx}, excluding test files, fixtures, mocks, and seed directories.

Example violation:

const DEFAULT_USER = DEMO_USER
const plan = DEMO_PLAN
const email = "demo@example.com"

Example compliant:

const DEFAULT_USER = process.env.DEFAULT_USER_ID
const plan = await getPlanFromDatabase(userId)
const email = user.email

PROD-002: Domain Allowlist Enforcement

Source code must not contain placeholder or development-only domain references.

Forbidden patterns:

Pattern Message
/https?:\/\/example\.com/ Placeholder domain – use real domain
/https?:\/\/localhost[:\/"']/ Localhost reference – use environment variable
/https?:\/\/127\.0\.0\.1/ Loopback address – use environment variable
/https?:\/\/placeholder\./i Placeholder domain – use real domain
/https?:\/\/todo\./i TODO domain – replace with actual domain
/https?:\/\/changeme\./i Changeme domain – replace with actual domain

Scope: src/**/*.{ts,js,tsx,jsx}, excluding test files.

Example violation:

const API_URL = "https://example.com/api"
const BASE_URL = "http://localhost:3000"

Example compliant:

const API_URL = process.env.API_URL
const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL

PROD-003: No Hardcoded IDs in Production Code

Source code must not contain hardcoded UUIDs, user IDs, tenant IDs, or org IDs.

Forbidden patterns:

Pattern Message
/['"][0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}['"]/ Hardcoded UUID – use configuration or database lookup
/user_id\s*[:=]\s*['"][a-zA-Z0-9_-]{10,}['"]/ Hardcoded user ID – use authenticated session or lookup
/tenant_id\s*[:=]\s*['"][a-zA-Z0-9_-]{10,}['"]/ Hardcoded tenant ID – use session context or lookup
/org_id\s*[:=]\s*['"][a-zA-Z0-9_-]{10,}['"]/ Hardcoded org ID – use session context or lookup

Scope: src/**/*.{ts,js,tsx,jsx}, excluding test files, fixtures, and migration directories.

Example violation:

const adminUser = "550e8400-e29b-41d4-a716-446655440000"
const tenant_id = "acme_corp_12345"

Example compliant:

const adminUser = await getAdminUser(orgId)
const tenant_id = session.tenantId

Quick Reference

ID Rule What It Catches
PROD-001 No demo/mock data DEMO_USER, MOCK_DATA, fakeAccount, seed assignments
PROD-002 Domain allowlist localhost, example.com, placeholder.dev
PROD-003 No hardcoded IDs UUID literals, user_id = "abc...", tenant_id = "..."

Installation

Copy the default contract template to your project:

cp Specflow/templates/contracts/production_readiness_defaults.yml docs/contracts/

Then run contract tests to verify:

npm test -- contracts

The contract expects a test file at src/__tests__/contracts/production_readiness_defaults.test.ts. You can generate this from the contract YAML using the contract-test-generator agent, or write it manually following the patterns in the contract schema reference.


Compliance Checklist

Before editing production source files, check:

Question If Yes
Are you referencing a specific user, tenant, or org ID? Use environment variables, database lookups, or session context. Never hardcode IDs.
Are you adding a URL or domain reference? Use environment variables for base URLs. Verify the domain is real and not a placeholder.
Are you adding demo or test data? Move to seed scripts, fixtures, or test files. Production code must not contain mock data.

Extending the Defaults

Add project-specific rules alongside the defaults using the same PROD-xxx prefix:

# docs/contracts/production_readiness_custom.yml
contract_meta:
  id: production_readiness_custom
  version: 1
  covers_reqs:
    - PROD-004
    - PROD-005
  owner: "platform-team"

rules:
  non_negotiable:
    - id: PROD-004
      title: "No test Stripe keys in production code"
      scope:
        - "src/**/*.{ts,js}"
        - "!src/**/*.test.*"
      behavior:
        forbidden_patterns:
          - pattern: /sk_test_[A-Za-z0-9]{24,}/
            message: "Test Stripe secret key in production code"
          - pattern: /pk_test_[A-Za-z0-9]{24,}/
            message: "Test Stripe publishable key in production code"

    - id: PROD-005
      title: "No TODO_REPLACE markers"
      scope:
        - "src/**/*.{ts,js,tsx,jsx}"
      behavior:
        forbidden_patterns:
          - pattern: /TODO_REPLACE/
            message: "Unreplaced TODO marker in production code"

Overriding Rules

Disable specific rules in .specflow/config.json:

{
  "disabled_rules": ["PROD-003"],
  "rule_overrides": {
    "PROD-002": {
      "severity": "important",
      "note": "We allow localhost in development-only config files"
    }
  }
}

The override protocol requires explicit human approval. An LLM agent cannot override non-negotiable rules unless the user says:

override_contract: production_readiness_defaults

Relationship to Other Gates

Production readiness gates complement the other default contract templates:

Gate Focus Contract File
Security (SEC-xxx) Secrets, XSS, SQL injection, eval security_defaults.yml
Accessibility (A11Y-xxx) Alt text, aria-labels, tab order accessibility_defaults.yml
Production Readiness (PROD-xxx) Demo data, placeholder domains, hardcoded IDs production_readiness_defaults.yml

All three can be installed independently. Together they provide baseline coverage for the most common issues that reach production.



View on GitHub