ReadmeBuddy LogoReadmeBuddy
Back to Blog

Documenting Environment Variables: Secrets, Examples, and Best Practices

ReadmeBuddy Team
Documenting Environment Variables: Secrets, Examples, and Best Practices

Shipping an application without clear environment variable documentation is a recipe for developer frustration. But how do you provide essential setup information without accidentally exposing sensitive secrets? This is a delicate balance, and getting it wrong can lead to security breaches or wasted developer time.

The Silent Killer: Undocumented Environment Variables

Imagine joining a new project. You pull the repo, run npm install, and then... nothing. The app fails with cryptic errors like "DATABASE_URL not found" or "API_KEY undefined." You hunt through code, ask around, and eventually piece together the necessary ENV variables. This is a common, frustrating scenario.

Good documentation of environment variables is crucial for:

  • Developer Onboarding: New team members can get started quickly.
  • Deployment Reliability: Ensures all necessary configurations are present in different environments (dev, staging, prod).
  • Maintainability: Helps future self or others understand why a variable exists.

The challenge? Many environment variables are secrets: API keys, database credentials, authentication tokens. Exposing these, even in internal documentation, is a significant security risk.

The Pitfalls: What NOT to Do

Let's be clear about what constitutes poor practice when documenting environment variables. Avoid these at all costs:

  • Hardcoding Secrets in Code: This is a cardinal sin. Never embed API_KEY = "sk_live_..." directly into your source code.
  • Listing Live Secrets in README.md: A common mistake. While well-intentioned, putting actual production or even development secrets directly into a public or even internal Git repository is a massive security vulnerability. Anyone with access to the repo's history can find it.
  • Overly Vague Descriptions: "API_KEY - The API key." This tells developers what it is, but not which API, where to get it, or what format it should be in.
  • Empty Documentation: Simply having a section called "Environment Variables" with no content is unhelpful.

The Secure Approach: Example-Driven Documentation

The core principle is to document what is needed, why it's needed, and how to get/format it, without ever including a live secret value.

1. The .env.example File

This is your frontline defense and most practical solution. Create a file named .env.example at the root of your project. This file lists all required environment variables with placeholder values and brief descriptions.

# Required for database connection
# Get this from your cloud provider or local setup instructions.
DATABASE_URL="postgres://user:password@host:port/database"

# Stripe API Key (for payment processing)
# Use a test key for development.
# Format: sk_test_xxxxxxxxxxxxxxxxxxxxxxxx
STRIPE_SECRET_KEY="sk_test_..."

# Your application's JWT secret
# Generate a strong, random string (e.g., using `openssl rand -base64 32`)
JWT_SECRET="YOUR_SUPER_SECRET_RANDOM_STRING_HERE"

# AWS S3 Bucket Name for file uploads
# Must be globally unique.
S3_BUCKET_NAME="your-app-uploads-dev"

# AWS Region for S3 bucket
# e.g., us-east-1
AWS_REGION="us-east-1"

Crucially, developers copy this to .env (which is typically Git-ignored) and fill in their own specific values.

2. Comprehensive README.md Descriptions

Your README.md should elaborate on the variables listed in .env.example. For each variable, provide:

  • Name: The exact variable name.
  • Purpose: What does it do? Which part of the application uses it?
  • Source/How to Obtain: Where does a developer get this value? (e.g., "From the AWS IAM console," "Generated via openssl," "Provided by your Stripe dashboard").
  • Format/Constraints: What kind of value is expected? (e.g., "a valid URL," "a 32-character hexadecimal string," "a boolean: true or false").
  • Example (Placeholder): Reiterate a placeholder or a generic example.
  • Default Value (if applicable): If the variable has a fallback in code, state it.
  • Sensitivity: Briefly mention if it's a secret and must not be committed.

Here's an example README.md snippet:


## Environment Variables

This project requires the following environment variables to be set. You can use a `.env` file in the root of the project (copy from `.env.example`).

-   **`DATABASE_URL`**
    -   **Purpose:** Connects the application to the PostgreSQL database.
    -   **Source:** Obtain this from your database hosting provider (e.g., Heroku Postgres, AWS RDS) or your local Docker setup.
    -   **Format:** A standard PostgreSQL connection string (e.g., `postgres://user:password@host:port/database`).
    -   **Example:** `postgres://devuser:devpass@localhost:5432/myapp_dev`
    -   **Sensitivity:** Contains database credentials; never commit to Git.

-   **`STRIPE_SECRET_KEY`**
    -   **Purpose:** Authenticates requests to the Stripe API for payment processing.
    -   **Source:** Find this in your Stripe Dashboard under "Developers > API keys." Use a *test* key for local development.
    -   **Format:** Starts with `sk_` (e.g., `sk_test_XXXXXXXXXXXXXXXXXXXXXXXX`).
    -   **Example:** `sk_test_ABCDEFGHIJKLMNOPQRSTUVWXYZ`
    -   **Sensitivity:** This is a highly sensitive API key; treat it as a secret.

-   **`JWT_SECRET`**
    -   **Purpose:** Used to sign and verify JSON Web Tokens for user authentication.
    -   **Source:** Generate a strong, random string. A good option is `openssl rand -base64 32`.
    -   **Format:** Any strong alphanumeric string.
    -   **Example:** `y7#sJ@p0V$cR*gW!eQ&mZ^bX%dN8uH6L`
    -   **Sensitivity:** Critical for application security; keep absolutely secret.

Beyond Documentation: Robust Secrets Management

While excellent documentation prevents accidental leaks, it doesn't protect against determined attackers if secrets are improperly stored. Consider these practices for production environments:

  • Dedicated Secrets Managers: For production, never store secrets directly in .env files on servers. Use services like:
    • AWS Secrets Manager
    • Google Cloud Secret Manager
    • HashiCorp Vault
    • Azure Key Vault
      These tools provide secure storage, access control, rotation, and auditing.
  • Environment Variables for Deployment: In CI/CD pipelines and production environments, configure environment variables directly through your deployment platform (e.g., Heroku Config Vars, Kubernetes Secrets, Vercel Environment Variables). These platforms usually offer secure ways to manage secrets.
  • Principle of Least Privilege: Ensure that only the necessary services and individuals have access to specific secrets.

Automating Documentation

Tools like ReadmeBuddy can help streamline this process. By defining your environment variables once with their descriptions, purposes, and example formats, you can generate consistent .env.example files and README.md sections automatically, reducing manual effort and the risk of outdated or incomplete documentation. This ensures every developer, new or old, has immediate access to accurate setup instructions without ever touching a live secret.

Conclusion

Documenting environment variables without leaking secrets is a fundamental practice for secure and efficient software development. By adopting .env.example files, providing comprehensive placeholder descriptions in your README.md, and leveraging robust secrets management solutions for production, you can foster a productive developer experience while safeguarding your application's most sensitive information. Make this a priority in your projects – your future self and your team will thank you.