---
name: env-var-naming
source: https://app.decimal.ai/s/env-var-naming@1/SKILL.md
source_sha256: 52e0a594b0bf
---

# Environment variable naming

## Contract

Enforces the standard identifier form for environment variables: UPPER_SNAKE_CASE names that start with
a letter, are namespaced under an owning app or component prefix, and tell related values apart by a
descriptive prefix rather than a numbered suffix. Apply when choosing what to *call* an app's
configuration variables — not when deciding whether a value belongs in an env var at all, and not when
reading or validating one at runtime.

## Rules

1. **Case — UPPER_SNAKE_CASE.** Every letter is uppercase; words within a name are joined by a single
   underscore. No lowercase, no camelCase, no PascalCase, no hyphens: `DATABASE_HOST`, never `dbHost`
   or `database-host`.

2. **Character set and first character.** A name begins with a letter (A–Z); after that it may contain
   only letters, digits, and underscores — no hyphens, dots, spaces, or other punctuation. A value
   whose natural name would start with a digit spells that digit out instead: a "7-zip" binary path
   becomes `SEVENZIP_PATH`, never `7ZIP_PATH`, because a leading digit is not a portable identifier.

3. **Namespace every name under its owner.** Prefix each variable with the app or component that owns
   it. Bare, generic names — `HOST`, `PORT`, `URL`, `KEY`, `SECRET`, `TOKEN`, `TIMEOUT`, `DEBUG` —
   collide the instant a second subsystem needs the same word. Write `POSTMARK_API_TOKEN`, never
   `TOKEN`; `SEARCH_CLUSTER_HOST`, never `HOST`. The owning prefix comes first, then the specific detail.

4. **Group by prefix, not by a number.** When several instances of one thing exist, distinguish them
   with a descriptive segment, never a counter. `CACHE_SESSION_URL` / `CACHE_RATELIMIT_URL`, never
   `CACHE_1_URL` / `CACHE_2_URL`; `MAIL_PRIMARY_HOST` / `MAIL_BACKUP_HOST`, never `MAIL_HOST_1`. A
   number tells the reader nothing about which one is which.

5. **Boolean flags.** Name a boolean as an affirmative predicate with an `_ENABLED` (or `_REQUIRED`)
   suffix, and give it one consistent literal value across the entire app: pick `true`/`false` (or
   `1`/`0`) and never mix `yes`, `on`, `True`, and `1` for flags of the same kind. Write
   `FEATURE_BETA_ENABLED=true`, never a bare `BETA=yes` sitting next to a `DEBUG=on`.

## Worked examples

Each shows the base's wrong default → the conforming name.

```
BEFORE  redis.url = ...            (dotted, lowercase)
        mailChimpApiKey = ...      (camelCase)
AFTER   REDIS_URL = ...
        MAILCHIMP_API_KEY = ...
```
Rule 1–2: uppercase, underscores, no dots or mixed case.

```
BEFORE  KEY = ...                  (bare, collides)
        HOST = ...
AFTER   POSTMARK_API_TOKEN = ...
        SEARCH_CLUSTER_HOST = ...
```
Rule 3: each name namespaced under the component that owns it, no bare generics.

```
BEFORE  CACHE_1_URL = ...          (numbered)
        CACHE_2_URL = ...
AFTER   CACHE_SESSION_URL = ...
        CACHE_RATELIMIT_URL = ...
```
Rule 4: a descriptive segment, not a counter, distinguishes the two caches.

```
BEFORE  1PASSWORD_TOKEN = ...      (leading digit)
AFTER   ONEPASSWORD_SERVICE_TOKEN = ...
```
Rule 2: a name may not begin with a digit; spell it out.

```
BEFORE  DEBUG = on                 (bare noun, ad-hoc value)
        BETA = yes
AFTER   APP_DEBUG_ENABLED = true
        FEATURE_BETA_ENABLED = true
```
Rule 5: predicate names, one consistent literal value.

## Edge cases & exceptions

- **A well-known single-word tool name** (e.g. a `PATH`-like variable the OS already defines) is left
  as the platform defines it; the rule governs the variables *your* app introduces.
- **Deeply nested components** still read owner-first, general-to-specific:
  `PAYMENTS_STRIPE_WEBHOOK_SECRET`, not `WEBHOOK_SECRET_STRIPE_PAYMENTS`.
- **A value that is genuinely global to the app** still takes the app prefix (`APP_LOG_LEVEL`), so it
  never sits in the namespace as a bare `LOG_LEVEL` another library might also claim.
- **Acronyms** stay uppercase and are separated like any word: `AWS_S3_BUCKET`, not `AWSS3BUCKET`.

## Do / Don't

- Do write every name in uppercase with underscores. Don't use camelCase, hyphens, or dots.
- Do start every name with a letter. Don't let a name begin with a digit — spell the digit out.
- Do prefix each variable with its owning component. Don't ship bare `HOST`, `KEY`, or `TIMEOUT`.
- Do distinguish instances with a descriptive prefix. Don't tell them apart with `_1` / `_2`.
- Do name booleans as `_ENABLED` predicates with one consistent value. Don't mix `yes`, `on`, and `1`.

## Common mistakes

- Bare generic names (`HOST`, `PORT`, `KEY`) that collide across subsystems — the default.
- Numeric instance suffixes (`SERVICE_1_URL`, `SERVICE_2_URL`) instead of descriptive prefixes.
- camelCase, hyphens, or dots carried over from a framework or a legacy config file.
- A name that begins with a digit because the service or feature name did.
- Boolean flags named as bare nouns (`SSL`, `RETRY`) or set with a jumble of `yes` / `on` / `1`.

## Quick checklist

- [ ] Uppercase letters, digits, underscores only; single-underscore word separators.
- [ ] Starts with a letter, never a digit.
- [ ] Prefixed under its owning app or component — no bare generic names.
- [ ] Related instances told apart by a descriptive prefix, not `_1` / `_2`.
- [ ] Booleans are `_ENABLED` predicates with one consistent literal value app-wide.
