Configuration, Environment, and Runtime Context

Explain how to manage environment variables, secrets, feature flags, and runtime-specific configuration. This section should connect function design to deployment hygiene.

Configuration is part of function design, not a deployment afterthought. In serverless systems, handlers often run in several environments, under several triggers, with different permissions and different surrounding managed services. If configuration is vague, duplicated, or mixed with secrets carelessly, the function boundary becomes harder to deploy and harder to trust.

The strongest configuration model is explicit about four categories:

  • static non-sensitive settings
  • sensitive values such as secrets or keys
  • environment-specific resource names and endpoints
  • runtime context such as request IDs, trigger metadata, or tenant context

These should not all be handled in the same way.

    flowchart TD
	    A["Runtime input"] --> B["Static config"]
	    A --> C["Secrets"]
	    A --> D["Environment-specific resource names"]
	    A --> E["Per-invocation context"]
	    C --> F["Secrets service or secure injection"]
	    B --> G["Safe env vars or config files"]
	    D --> H["Deployment-managed mapping"]
	    E --> I["Request, event, or workflow metadata"]

What to notice:

  • not all configuration belongs in environment variables
  • secrets and runtime context are different concerns
  • deployment hygiene improves when each category has an explicit path

Environment Variables Are Useful, Not Sufficient

Environment variables are a convenient place for non-sensitive runtime settings such as:

  • feature toggles that are deployment-scoped
  • resource identifiers
  • timeout or behavior toggles
  • log verbosity settings

They become risky when teams use them as a dumping ground for secrets, opaque JSON blobs, or values no one can track cleanly across environments.

Secrets Need a Separate Path

Secrets are not just “important config.” They need stronger handling because they require:

  • limited access
  • auditability
  • rotation support
  • reduced exposure in logs and debugging output

That is why secrets services or secure injection mechanisms are usually better than embedding secrets directly in plain environment variables or source-controlled config files.

Runtime Context Is Not Configuration

Teams often blur runtime context with config. They are different.

Configuration answers:

  • Which queue should this function publish to?
  • Which feature flags are enabled in this environment?

Runtime context answers:

  • Which request or event triggered this invocation?
  • Which tenant or customer is in scope?
  • Which correlation ID should be logged?

Confusing the two leads to functions that are harder to test and harder to reason about.

A Healthy Configuration Loader

This example separates stable config from secrets and per-invocation context.

 1type AppConfig = {
 2  orderQueueName: string;
 3  enableFraudChecks: boolean;
 4};
 5
 6export function loadConfig(env: NodeJS.ProcessEnv): AppConfig {
 7  return {
 8    orderQueueName: env.ORDER_QUEUE_NAME ?? "order-jobs",
 9    enableFraudChecks: env.ENABLE_FRAUD_CHECKS === "true",
10  };
11}
 1export async function handle(event: OrderEvent) {
 2  const config = loadConfig(process.env);
 3  const apiKey = await secrets.get("fraud-service-key");
 4
 5  logger.info("processing-order", {
 6    orderId: event.orderId,
 7    correlationId: event.correlationId,
 8  });
 9
10  if (config.enableFraudChecks) {
11    await fraudClient.check(event.orderId, apiKey);
12  }
13
14  await queue.publish(config.orderQueueName, event);
15}

What this demonstrates:

  • non-sensitive config is loaded explicitly
  • the secret is resolved through a dedicated secure path
  • runtime context comes from the event, not from hidden global state

Environment-Specific Design

Serverless systems often run across development, test, staging, and production. Strong configuration design keeps those environments separate without forcing code changes. That usually means:

  • different resource names per environment
  • different feature flags where justified
  • the same code path with environment-specific configuration mapping

What should not vary casually:

  • core business semantics
  • hidden branching based on ungoverned environment flags

Too much environment-specific branching becomes a configuration anti-pattern of its own.

Common Mistakes

  • storing secrets directly in plain config without a dedicated secrets path
  • letting handlers read many implicit environment values with no typed loader
  • using environment flags to create large hidden behavior differences between stages
  • logging runtime secrets or sensitive config accidentally
  • mixing per-request context with deployment-scoped settings

Design Review Question

A team has one function with twelve environment variables, three embedded API keys, several stage-specific conditionals, and no clear distinction between static settings and request context. The function still “works.” What should be fixed first?

The strongest first fix is to separate concerns: create a typed loader for stable non-sensitive config, move secrets to a proper secure path, and keep request or event context in the invocation payload rather than in ambient global variables. The issue is not just tidiness. Weak configuration structure makes security, testing, rotation, and deployment reasoning worse at the same time.

Check Your Understanding

Loading quiz…
Revised on Thursday, April 23, 2026