config-manager

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Configuration Manager Skill

配置管理Skill

Overview

概述

This skill helps you design and implement robust configuration management for applications. Covers environment variables, config files, secrets management, validation, type safety, and multi-environment deployments.
本Skill可帮助你设计并实现健壮的应用配置管理方案,涵盖环境变量、配置文件、密钥管理、验证、类型安全以及多环境部署等内容。

Configuration Philosophy

配置理念

Twelve-Factor App Principles

十二要素应用原则

  1. Store config in the environment: Separate config from code
  2. Strict separation: Config that varies between deploys should be in env vars
  3. No secrets in code: Ever. Period.
  1. 将配置存储在环境中:将配置与代码分离
  2. 严格分离:不同部署环境间有差异的配置应放在环境变量中
  3. 代码中绝不包含密钥:永远不要这么做,没有例外。

Configuration Hierarchy

配置优先级(从高到低)

Priority (highest to lowest):
1. Command-line arguments
2. Environment variables
3. Environment-specific config files (.env.local)
4. Default config files (.env)
5. Application defaults in code
Priority (highest to lowest):
1. Command-line arguments
2. Environment variables
3. Environment-specific config files (.env.local)
4. Default config files (.env)
5. Application defaults in code

What Goes Where

配置内容分类

  • Environment Variables: API keys, database URLs, feature flags
  • Config Files: Non-sensitive defaults, complex structures
  • Secrets Manager: Production credentials, API tokens
  • Code: Application logic, never secrets
  • 环境变量:API密钥、数据库URL、功能开关
  • 配置文件:非敏感默认值、复杂结构
  • 密钥管理器:生产环境凭证、API令牌
  • 代码:应用逻辑,绝不包含密钥

Environment Variables

环境变量

.env File Structure

.env文件结构

bash
undefined
bash
undefined

.env.example - Commit this file as documentation

.env.example - Commit this file as documentation

Copy to .env and fill in values

Copy to .env and fill in values

===================

===================

Application

Application

===================

===================

NODE_ENV=development PORT=3000 APP_URL=http://localhost:3000
NODE_ENV=development PORT=3000 APP_URL=http://localhost:3000

===================

===================

Database

Database

===================

===================

DATABASE_URL=postgresql://user:password@localhost:5432/mydb DATABASE_POOL_SIZE=10
DATABASE_URL=postgresql://user:password@localhost:5432/mydb DATABASE_POOL_SIZE=10

===================

===================

Authentication

Authentication

===================

===================

Get from Supabase Dashboard > Settings > API

Get from Supabase Dashboard > Settings > API

NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key SUPABASE_SERVICE_ROLE_KEY=your-service-role-key

===================

===================

External Services

External Services

===================

===================

STRIPE_SECRET_KEY=sk_test_xxx STRIPE_WEBHOOK_SECRET=whsec_xxx NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_xxx
STRIPE_SECRET_KEY=sk_test_xxx STRIPE_WEBHOOK_SECRET=whsec_xxx NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_xxx

===================

===================

Email

Email

===================

===================

RESEND_API_KEY=re_xxx EMAIL_FROM=noreply@example.com
RESEND_API_KEY=re_xxx EMAIL_FROM=noreply@example.com

===================

===================

Feature Flags

Feature Flags

===================

===================

ENABLE_ANALYTICS=true ENABLE_BETA_FEATURES=false
undefined
ENABLE_ANALYTICS=true ENABLE_BETA_FEATURES=false
undefined

.gitignore Configuration

.gitignore配置

gitignore
undefined
gitignore
undefined

Environment files

Environment files

.env .env.local .env.*.local .env.development.local .env.test.local .env.production.local
.env .env.local .env.*.local .env.development.local .env.test.local .env.production.local

Keep example file

Keep example file

!.env.example
undefined
!.env.example
undefined

Multi-Environment Setup

多环境设置

bash
undefined
bash
undefined

.env.development

.env.development

NODE_ENV=development DATABASE_URL=postgresql://localhost:5432/myapp_dev LOG_LEVEL=debug
NODE_ENV=development DATABASE_URL=postgresql://localhost:5432/myapp_dev LOG_LEVEL=debug

.env.test

.env.test

NODE_ENV=test DATABASE_URL=postgresql://localhost:5432/myapp_test LOG_LEVEL=error
NODE_ENV=test DATABASE_URL=postgresql://localhost:5432/myapp_test LOG_LEVEL=error

.env.production

.env.production

NODE_ENV=production DATABASE_URL=${DATABASE_URL} # Set in deployment platform LOG_LEVEL=info
undefined
NODE_ENV=production DATABASE_URL=${DATABASE_URL} # Set in deployment platform LOG_LEVEL=info
undefined

Type-Safe Configuration

类型安全配置

Zod Schema Validation

Zod Schema验证

typescript
// src/config/env.ts
import { z } from 'zod';

// Define schema
const envSchema = z.object({
  // Node environment
  NODE_ENV: z.enum(['development', 'test', 'production']).default('development'),

  // Server
  PORT: z.coerce.number().default(3000),
  APP_URL: z.string().url(),

  // Database
  DATABASE_URL: z.string().url(),
  DATABASE_POOL_SIZE: z.coerce.number().min(1).max(100).default(10),

  // Supabase
  NEXT_PUBLIC_SUPABASE_URL: z.string().url(),
  NEXT_PUBLIC_SUPABASE_ANON_KEY: z.string().min(1),
  SUPABASE_SERVICE_ROLE_KEY: z.string().min(1),

  // Stripe (optional in development)
  STRIPE_SECRET_KEY: z.string().startsWith('sk_').optional(),
  STRIPE_WEBHOOK_SECRET: z.string().startsWith('whsec_').optional(),

  // Feature flags
  ENABLE_ANALYTICS: z.coerce.boolean().default(false),
  ENABLE_BETA_FEATURES: z.coerce.boolean().default(false),
});

// Type export
export type Env = z.infer<typeof envSchema>;

// Parse and validate
function loadEnv(): Env {
  const result = envSchema.safeParse(process.env);

  if (!result.success) {
    console.error('Invalid environment variables:');
    console.error(result.error.format());
    process.exit(1);
  }

  return result.data;
}

// Singleton export
export const env = loadEnv();
typescript
// src/config/env.ts
import { z } from 'zod';

// Define schema
const envSchema = z.object({
  // Node environment
  NODE_ENV: z.enum(['development', 'test', 'production']).default('development'),

  // Server
  PORT: z.coerce.number().default(3000),
  APP_URL: z.string().url(),

  // Database
  DATABASE_URL: z.string().url(),
  DATABASE_POOL_SIZE: z.coerce.number().min(1).max(100).default(10),

  // Supabase
  NEXT_PUBLIC_SUPABASE_URL: z.string().url(),
  NEXT_PUBLIC_SUPABASE_ANON_KEY: z.string().min(1),
  SUPABASE_SERVICE_ROLE_KEY: z.string().min(1),

  // Stripe (optional in development)
  STRIPE_SECRET_KEY: z.string().startsWith('sk_').optional(),
  STRIPE_WEBHOOK_SECRET: z.string().startsWith('whsec_').optional(),

  // Feature flags
  ENABLE_ANALYTICS: z.coerce.boolean().default(false),
  ENABLE_BETA_FEATURES: z.coerce.boolean().default(false),
});

// Type export
export type Env = z.infer<typeof envSchema>;

// Parse and validate
function loadEnv(): Env {
  const result = envSchema.safeParse(process.env);

  if (!result.success) {
    console.error('Invalid environment variables:');
    console.error(result.error.format());
    process.exit(1);
  }

  return result.data;
}

// Singleton export
export const env = loadEnv();

Environment Validation at Build Time

构建时环境验证

typescript
// src/config/validate-env.ts
import { z } from 'zod';

// Client-side env (exposed to browser)
const clientEnvSchema = z.object({
  NEXT_PUBLIC_SUPABASE_URL: z.string().url(),
  NEXT_PUBLIC_SUPABASE_ANON_KEY: z.string().min(1),
  NEXT_PUBLIC_APP_URL: z.string().url(),
});

// Server-side env (never exposed to browser)
const serverEnvSchema = z.object({
  DATABASE_URL: z.string(),
  SUPABASE_SERVICE_ROLE_KEY: z.string(),
  STRIPE_SECRET_KEY: z.string().optional(),
});

// Combined
const envSchema = clientEnvSchema.merge(serverEnvSchema);

export function validateEnv() {
  // Only validate server-side env on server
  if (typeof window !== 'undefined') {
    return clientEnvSchema.parse({
      NEXT_PUBLIC_SUPABASE_URL: process.env.NEXT_PUBLIC_SUPABASE_URL,
      NEXT_PUBLIC_SUPABASE_ANON_KEY: process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
      NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL,
    });
  }

  return envSchema.parse(process.env);
}
typescript
// src/config/validate-env.ts
import { z } from 'zod';

// Client-side env (exposed to browser)
const clientEnvSchema = z.object({
  NEXT_PUBLIC_SUPABASE_URL: z.string().url(),
  NEXT_PUBLIC_SUPABASE_ANON_KEY: z.string().min(1),
  NEXT_PUBLIC_APP_URL: z.string().url(),
});

// Server-side env (never exposed to browser)
const serverEnvSchema = z.object({
  DATABASE_URL: z.string(),
  SUPABASE_SERVICE_ROLE_KEY: z.string(),
  STRIPE_SECRET_KEY: z.string().optional(),
});

// Combined
const envSchema = clientEnvSchema.merge(serverEnvSchema);

export function validateEnv() {
  // Only validate server-side env on server
  if (typeof window !== 'undefined') {
    return clientEnvSchema.parse({
      NEXT_PUBLIC_SUPABASE_URL: process.env.NEXT_PUBLIC_SUPABASE_URL,
      NEXT_PUBLIC_SUPABASE_ANON_KEY: process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
      NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL,
    });
  }

  return envSchema.parse(process.env);
}

T3 Env Pattern (Next.js)

T3 Env模式(Next.js)

typescript
// src/env.mjs
import { createEnv } from '@t3-oss/env-nextjs';
import { z } from 'zod';

export const env = createEnv({
  /**
   * Server-side environment variables
   */
  server: {
    DATABASE_URL: z.string().url(),
    NODE_ENV: z.enum(['development', 'test', 'production']),
    SUPABASE_SERVICE_ROLE_KEY: z.string(),
    STRIPE_SECRET_KEY: z.string().startsWith('sk_'),
  },

  /**
   * Client-side environment variables (exposed to browser)
   * Prefix with NEXT_PUBLIC_
   */
  client: {
    NEXT_PUBLIC_SUPABASE_URL: z.string().url(),
    NEXT_PUBLIC_SUPABASE_ANON_KEY: z.string(),
    NEXT_PUBLIC_APP_URL: z.string().url(),
  },

  /**
   * Runtime values
   */
  runtimeEnv: {
    DATABASE_URL: process.env.DATABASE_URL,
    NODE_ENV: process.env.NODE_ENV,
    SUPABASE_SERVICE_ROLE_KEY: process.env.SUPABASE_SERVICE_ROLE_KEY,
    STRIPE_SECRET_KEY: process.env.STRIPE_SECRET_KEY,
    NEXT_PUBLIC_SUPABASE_URL: process.env.NEXT_PUBLIC_SUPABASE_URL,
    NEXT_PUBLIC_SUPABASE_ANON_KEY: process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
    NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL,
  },

  /**
   * Skip validation in certain environments
   */
  skipValidation: !!process.env.SKIP_ENV_VALIDATION,
});
typescript
// src/env.mjs
import { createEnv } from '@t3-oss/env-nextjs';
import { z } from 'zod';

export const env = createEnv({
  /**
   * Server-side environment variables
   */
  server: {
    DATABASE_URL: z.string().url(),
    NODE_ENV: z.enum(['development', 'test', 'production']),
    SUPABASE_SERVICE_ROLE_KEY: z.string(),
    STRIPE_SECRET_KEY: z.string().startsWith('sk_'),
  },

  /**
   * Client-side environment variables (exposed to browser)
   * Prefix with NEXT_PUBLIC_
   */
  client: {
    NEXT_PUBLIC_SUPABASE_URL: z.string().url(),
    NEXT_PUBLIC_SUPABASE_ANON_KEY: z.string(),
    NEXT_PUBLIC_APP_URL: z.string().url(),
  },

  /**
   * Runtime values
   */
  runtimeEnv: {
    DATABASE_URL: process.env.DATABASE_URL,
    NODE_ENV: process.env.NODE_ENV,
    SUPABASE_SERVICE_ROLE_KEY: process.env.SUPABASE_SERVICE_ROLE_KEY,
    STRIPE_SECRET_KEY: process.env.STRIPE_SECRET_KEY,
    NEXT_PUBLIC_SUPABASE_URL: process.env.NEXT_PUBLIC_SUPABASE_URL,
    NEXT_PUBLIC_SUPABASE_ANON_KEY: process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
    NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL,
  },

  /**
   * Skip validation in certain environments
   */
  skipValidation: !!process.env.SKIP_ENV_VALIDATION,
});

Configuration Files

配置文件

JSON Configuration

JSON配置

typescript
// config/default.json
{
  "app": {
    "name": "My Application",
    "version": "1.0.0"
  },
  "server": {
    "port": 3000,
    "host": "localhost"
  },
  "cache": {
    "ttl": 3600,
    "maxSize": 1000
  },
  "features": {
    "darkMode": true,
    "betaFeatures": false
  }
}

// config/production.json (overrides default)
{
  "server": {
    "host": "0.0.0.0"
  },
  "cache": {
    "ttl": 86400
  }
}
typescript
// config/default.json
{
  "app": {
    "name": "My Application",
    "version": "1.0.0"
  },
  "server": {
    "port": 3000,
    "host": "localhost"
  },
  "cache": {
    "ttl": 3600,
    "maxSize": 1000
  },
  "features": {
    "darkMode": true,
    "betaFeatures": false
  }
}

// config/production.json (overrides default)
{
  "server": {
    "host": "0.0.0.0"
  },
  "cache": {
    "ttl": 86400
  }
}

Config Loader

配置加载器

typescript
// src/config/loader.ts
import { readFileSync, existsSync } from 'fs';
import { join } from 'path';
import { z } from 'zod';

const configSchema = z.object({
  app: z.object({
    name: z.string(),
    version: z.string(),
  }),
  server: z.object({
    port: z.number(),
    host: z.string(),
  }),
  cache: z.object({
    ttl: z.number(),
    maxSize: z.number(),
  }),
  features: z.object({
    darkMode: z.boolean(),
    betaFeatures: z.boolean(),
  }),
});

type Config = z.infer<typeof configSchema>;

function deepMerge(target: any, source: any): any {
  const result = { ...target };

  for (const key of Object.keys(source)) {
    if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
      result[key] = deepMerge(result[key] || {}, source[key]);
    } else {
      result[key] = source[key];
    }
  }

  return result;
}

export function loadConfig(): Config {
  const env = process.env.NODE_ENV || 'development';
  const configDir = join(process.cwd(), 'config');

  // Load default config
  const defaultPath = join(configDir, 'default.json');
  let config = JSON.parse(readFileSync(defaultPath, 'utf-8'));

  // Merge environment-specific config
  const envPath = join(configDir, `${env}.json`);
  if (existsSync(envPath)) {
    const envConfig = JSON.parse(readFileSync(envPath, 'utf-8'));
    config = deepMerge(config, envConfig);
  }

  // Merge local overrides (not committed)
  const localPath = join(configDir, 'local.json');
  if (existsSync(localPath)) {
    const localConfig = JSON.parse(readFileSync(localPath, 'utf-8'));
    config = deepMerge(config, localConfig);
  }

  // Validate
  return configSchema.parse(config);
}

export const config = loadConfig();
typescript
// src/config/loader.ts
import { readFileSync, existsSync } from 'fs';
import { join } from 'path';
import { z } from 'zod';

const configSchema = z.object({
  app: z.object({
    name: z.string(),
    version: z.string(),
  }),
  server: z.object({
    port: z.number(),
    host: z.string(),
  }),
  cache: z.object({
    ttl: z.number(),
    maxSize: z.number(),
  }),
  features: z.object({
    darkMode: z.boolean(),
    betaFeatures: z.boolean(),
  }),
});

type Config = z.infer<typeof configSchema>;

function deepMerge(target: any, source: any): any {
  const result = { ...target };

  for (const key of Object.keys(source)) {
    if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
      result[key] = deepMerge(result[key] || {}, source[key]);
    } else {
      result[key] = source[key];
    }
  }

  return result;
}

export function loadConfig(): Config {
  const env = process.env.NODE_ENV || 'development';
  const configDir = join(process.cwd(), 'config');

  // Load default config
  const defaultPath = join(configDir, 'default.json');
  let config = JSON.parse(readFileSync(defaultPath, 'utf-8'));

  // Merge environment-specific config
  const envPath = join(configDir, `${env}.json`);
  if (existsSync(envPath)) {
    const envConfig = JSON.parse(readFileSync(envPath, 'utf-8'));
    config = deepMerge(config, envConfig);
  }

  // Merge local overrides (not committed)
  const localPath = join(configDir, 'local.json');
  if (existsSync(localPath)) {
    const localConfig = JSON.parse(readFileSync(localPath, 'utf-8'));
    config = deepMerge(config, localConfig);
  }

  // Validate
  return configSchema.parse(config);
}

export const config = loadConfig();

Secrets Management

密钥管理

Local Development Secrets

本地开发密钥

bash
undefined
bash
undefined

Use a secrets manager locally too

Use a secrets manager locally too

Option 1: 1Password CLI

Option 1: 1Password CLI

op read "op://Development/MyApp/API_KEY"
op read "op://Development/MyApp/API_KEY"

Option 2: Doppler

Option 2: Doppler

doppler secrets download --no-file --format env > .env
doppler secrets download --no-file --format env > .env

Option 3: AWS SSM (for local AWS dev)

Option 3: AWS SSM (for local AWS dev)

aws ssm get-parameter --name "/myapp/dev/api-key" --with-decryption --query "Parameter.Value"
undefined
aws ssm get-parameter --name "/myapp/dev/api-key" --with-decryption --query "Parameter.Value"
undefined

Production Secrets with Vercel

Vercel生产环境密钥

bash
undefined
bash
undefined

Add secrets via CLI

Add secrets via CLI

vercel env add STRIPE_SECRET_KEY production vercel env add DATABASE_URL production
vercel env add STRIPE_SECRET_KEY production vercel env add DATABASE_URL production

Pull secrets to local .env

Pull secrets to local .env

vercel env pull .env.local
undefined
vercel env pull .env.local
undefined

Secrets in Docker

Docker中的密钥

yaml
undefined
yaml
undefined

docker-compose.yml

docker-compose.yml

version: '3.8' services: app: build: . environment: - NODE_ENV=production env_file: - .env secrets: - db_password - api_key
secrets: db_password: file: ./secrets/db_password.txt api_key: external: true # From Docker Swarm/secrets manager
undefined
version: '3.8' services: app: build: . environment: - NODE_ENV=production env_file: - .env secrets: - db_password - api_key
secrets: db_password: file: ./secrets/db_password.txt api_key: external: true # From Docker Swarm/secrets manager
undefined

AWS Secrets Manager Integration

AWS Secrets Manager集成

typescript
// src/config/secrets.ts
import {
  SecretsManagerClient,
  GetSecretValueCommand,
} from '@aws-sdk/client-secrets-manager';

const client = new SecretsManagerClient({ region: 'us-east-1' });

interface AppSecrets {
  database_url: string;
  stripe_secret_key: string;
  jwt_secret: string;
}

let cachedSecrets: AppSecrets | null = null;

export async function getSecrets(): Promise<AppSecrets> {
  if (cachedSecrets) {
    return cachedSecrets;
  }

  const secretId = process.env.AWS_SECRET_ID || 'myapp/production/secrets';

  const command = new GetSecretValueCommand({ SecretId: secretId });
  const response = await client.send(command);

  if (!response.SecretString) {
    throw new Error('Secret not found');
  }

  cachedSecrets = JSON.parse(response.SecretString);
  return cachedSecrets!;
}

// Usage
async function connectDatabase() {
  const secrets = await getSecrets();
  return createConnection(secrets.database_url);
}
typescript
// src/config/secrets.ts
import {
  SecretsManagerClient,
  GetSecretValueCommand,
} from '@aws-sdk/client-secrets-manager';

const client = new SecretsManagerClient({ region: 'us-east-1' });

interface AppSecrets {
  database_url: string;
  stripe_secret_key: string;
  jwt_secret: string;
}

let cachedSecrets: AppSecrets | null = null;

export async function getSecrets(): Promise<AppSecrets> {
  if (cachedSecrets) {
    return cachedSecrets;
  }

  const secretId = process.env.AWS_SECRET_ID || 'myapp/production/secrets';

  const command = new GetSecretValueCommand({ SecretId: secretId });
  const response = await client.send(command);

  if (!response.SecretString) {
    throw new Error('Secret not found');
  }

  cachedSecrets = JSON.parse(response.SecretString);
  return cachedSecrets!;
}

// Usage
async function connectDatabase() {
  const secrets = await getSecrets();
  return createConnection(secrets.database_url);
}

Feature Flags

功能开关

Simple Feature Flag System

简单功能开关系统

typescript
// src/config/features.ts
import { env } from './env';

export const features = {
  analytics: env.ENABLE_ANALYTICS,
  betaFeatures: env.ENABLE_BETA_FEATURES,
  darkMode: true,  // Always enabled

  // Computed flags
  get isProduction() {
    return env.NODE_ENV === 'production';
  },

  get enableDebugLogs() {
    return env.NODE_ENV === 'development' || env.DEBUG === 'true';
  },
} as const;

// Type-safe feature checking
export function isEnabled(feature: keyof typeof features): boolean {
  return Boolean(features[feature]);
}

// Usage
if (isEnabled('analytics')) {
  initAnalytics();
}
typescript
// src/config/features.ts
import { env } from './env';

export const features = {
  analytics: env.ENABLE_ANALYTICS,
  betaFeatures: env.ENABLE_BETA_FEATURES,
  darkMode: true,  // Always enabled

  // Computed flags
  get isProduction() {
    return env.NODE_ENV === 'production';
  },

  get enableDebugLogs() {
    return env.NODE_ENV === 'development' || env.DEBUG === 'true';
  },
} as const;

// Type-safe feature checking
export function isEnabled(feature: keyof typeof features): boolean {
  return Boolean(features[feature]);
}

// Usage
if (isEnabled('analytics')) {
  initAnalytics();
}

Environment-Aware Feature Flags

环境感知型功能开关

typescript
// src/config/feature-flags.ts
type Environment = 'development' | 'staging' | 'production';

interface FeatureConfig {
  enabled: boolean | Environment[];
  description: string;
}

const featureFlags: Record<string, FeatureConfig> = {
  newCheckout: {
    enabled: ['development', 'staging'],
    description: 'New checkout flow',
  },
  aiAssistant: {
    enabled: true,
    description: 'AI assistant feature',
  },
  experimentalApi: {
    enabled: ['development'],
    description: 'Experimental API endpoints',
  },
};

export function isFeatureEnabled(
  feature: keyof typeof featureFlags
): boolean {
  const config = featureFlags[feature];
  const currentEnv = process.env.NODE_ENV as Environment;

  if (typeof config.enabled === 'boolean') {
    return config.enabled;
  }

  return config.enabled.includes(currentEnv);
}
typescript
// src/config/feature-flags.ts
type Environment = 'development' | 'staging' | 'production';

interface FeatureConfig {
  enabled: boolean | Environment[];
  description: string;
}

const featureFlags: Record<string, FeatureConfig> = {
  newCheckout: {
    enabled: ['development', 'staging'],
    description: 'New checkout flow',
  },
  aiAssistant: {
    enabled: true,
    description: 'AI assistant feature',
  },
  experimentalApi: {
    enabled: ['development'],
    description: 'Experimental API endpoints',
  },
};

export function isFeatureEnabled(
  feature: keyof typeof featureFlags
): boolean {
  const config = featureFlags[feature];
  const currentEnv = process.env.NODE_ENV as Environment;

  if (typeof config.enabled === 'boolean') {
    return config.enabled;
  }

  return config.enabled.includes(currentEnv);
}

Configuration Patterns

配置模式

Singleton Configuration

单例配置

typescript
// src/config/index.ts
import { env } from './env';
import { loadConfig } from './loader';
import { features } from './features';

class AppConfig {
  private static instance: AppConfig;

  public readonly env = env;
  public readonly features = features;
  public readonly settings = loadConfig();

  private constructor() {
    // Freeze to prevent modifications
    Object.freeze(this);
  }

  static getInstance(): AppConfig {
    if (!AppConfig.instance) {
      AppConfig.instance = new AppConfig();
    }
    return AppConfig.instance;
  }

  // Helper methods
  get isDevelopment(): boolean {
    return this.env.NODE_ENV === 'development';
  }

  get isProduction(): boolean {
    return this.env.NODE_ENV === 'production';
  }

  get databaseUrl(): string {
    return this.env.DATABASE_URL;
  }
}

export const config = AppConfig.getInstance();
typescript
// src/config/index.ts
import { env } from './env';
import { loadConfig } from './loader';
import { features } from './features';

class AppConfig {
  private static instance: AppConfig;

  public readonly env = env;
  public readonly features = features;
  public readonly settings = loadConfig();

  private constructor() {
    // Freeze to prevent modifications
    Object.freeze(this);
  }

  static getInstance(): AppConfig {
    if (!AppConfig.instance) {
      AppConfig.instance = new AppConfig();
    }
    return AppConfig.instance;
  }

  // Helper methods
  get isDevelopment(): boolean {
    return this.env.NODE_ENV === 'development';
  }

  get isProduction(): boolean {
    return this.env.NODE_ENV === 'production';
  }

  get databaseUrl(): string {
    return this.env.DATABASE_URL;
  }
}

export const config = AppConfig.getInstance();

Runtime Configuration Updates

运行时配置更新

typescript
// src/config/runtime.ts
import { EventEmitter } from 'events';

class RuntimeConfig extends EventEmitter {
  private values: Map<string, any> = new Map();

  get<T>(key: string, defaultValue?: T): T | undefined {
    return this.values.get(key) ?? defaultValue;
  }

  set<T>(key: string, value: T): void {
    const oldValue = this.values.get(key);
    this.values.set(key, value);
    this.emit('change', { key, oldValue, newValue: value });
  }

  // Subscribe to changes
  onChange(callback: (change: { key: string; oldValue: any; newValue: any }) => void) {
    this.on('change', callback);
    return () => this.off('change', callback);
  }
}

export const runtimeConfig = new RuntimeConfig();

// Usage: Update config at runtime
runtimeConfig.set('maintenanceMode', true);

// Usage: React to changes
runtimeConfig.onChange(({ key, newValue }) => {
  if (key === 'maintenanceMode' && newValue) {
    showMaintenanceBanner();
  }
});
typescript
// src/config/runtime.ts
import { EventEmitter } from 'events';

class RuntimeConfig extends EventEmitter {
  private values: Map<string, any> = new Map();

  get<T>(key: string, defaultValue?: T): T | undefined {
    return this.values.get(key) ?? defaultValue;
  }

  set<T>(key: string, value: T): void {
    const oldValue = this.values.get(key);
    this.values.set(key, value);
    this.emit('change', { key, oldValue, newValue: value });
  }

  // Subscribe to changes
  onChange(callback: (change: { key: string; oldValue: any; newValue: any }) => void) {
    this.on('change', callback);
    return () => this.off('change', callback);
  }
}

export const runtimeConfig = new RuntimeConfig();

// Usage: Update config at runtime
runtimeConfig.set('maintenanceMode', true);

// Usage: React to changes
runtimeConfig.onChange(({ key, newValue }) => {
  if (key === 'maintenanceMode' && newValue) {
    showMaintenanceBanner();
  }
});

Validation Checklist

验证检查清单

Environment Setup

环境设置

  • .env.example
    with all variables documented
  • .gitignore
    excludes all
    .env
    files except example
  • Validation runs at application startup
  • Helpful error messages for missing/invalid config
  • Type definitions for all config values
  • 包含所有变量说明的
    .env.example
    文件
  • .gitignore
    排除所有
    .env
    文件,仅保留示例文件
  • 应用启动时运行配置验证
  • 针对缺失/无效配置提供清晰的错误提示
  • 所有配置值的类型定义

Security

安全

  • No secrets in version control
  • Secrets use appropriate secrets manager
  • Production secrets rotated regularly
  • Minimal exposure of sensitive values in logs
  • NEXT_PUBLIC_ prefix only for truly public values
  • 版本控制中不包含任何密钥
  • 使用合适的密钥管理器存储密钥
  • 定期轮换生产环境密钥
  • 日志中尽量减少敏感值的暴露
  • 仅对真正公开的值使用NEXT_PUBLIC_前缀

Developer Experience

开发者体验

  • Easy local setup (copy
    .env.example
    )
  • Clear documentation of each variable
  • Defaults for development environment
  • Config validation gives actionable errors
  • IDE autocomplete for config values
  • 本地设置简单(复制
    .env.example
    即可)
  • 每个变量都有清晰的说明文档
  • 开发环境提供默认值
  • 配置验证给出可操作的错误信息
  • 配置值支持IDE自动补全

Multi-Environment

多环境

  • Separate configs for dev/staging/production
  • Environment-specific overrides work correctly
  • Feature flags for gradual rollouts
  • Easy switching between environments
  • 为开发/预发布/生产环境分别配置独立的配置
  • 环境特定的覆盖配置可正常工作
  • 用于逐步发布的功能开关
  • 可轻松在不同环境间切换

When to Use This Skill

何时使用本Skill

Invoke this skill when:
  • Setting up configuration for a new project
  • Adding new environment variables
  • Implementing secrets management
  • Creating feature flag systems
  • Debugging configuration issues
  • Setting up multi-environment deployments
  • Migrating configuration between providers
  • Implementing runtime configuration changes
在以下场景中调用本Skill:
  • 为新项目设置配置
  • 添加新的环境变量
  • 实现密钥管理方案
  • 创建功能开关系统
  • 调试配置相关问题
  • 设置多环境部署
  • 在不同服务商之间迁移配置
  • 实现运行时配置变更