Environment Variable

Concept

A setting supplied from outside the program so the same code can use different values wherever it runs — addresses, keys, and anything else that changes by location.

Why it exists

These values used to be written straight into the code. The database address, the key issued by the payment provider — all of it sat in some file as a plain string. For one person writing one program that ran on one server, nothing about that was a problem.

The trouble surfaced once a developer's laptop, a test server and the real server became three separate places. All three run the same code but need to talk to different databases. So every deploy meant opening a file, editing the address, and editing it back afterward. Miss it once and the test server is touching real customer data. Almost every company has a version of the story where a script that generates test accounts was pointed at the production database and a few thousand dummy users landed in the customer list.

Keys were worse. Put a payment key in the code and push it to the repository and everyone with access to that repository now has the key. It stays on a departed employee's laptop, and if it ever reaches a public repository someone else is using it within minutes. Environment variables solve both problems the same way: take the values that change out of the code, and let whoever runs the program supply them at the time.

How it works

When a program starts, it reads name-and-value pairs that the operating system is holding. The code says only "fetch the value named DATABASE_URL"; what that name contains is decided by whoever runs it.

The flow has three steps.

  1. The code refers to the value by name. Instead of writing the address, it reads DATABASE_URL and uses whatever comes back.
  2. Whoever runs the program fills that name in. A local database address on a developer's laptop, the real address on the production server. The filling happens in a deployment tool's settings screen or in the command that starts the server.
  3. The program reads the values as it boots. They generally don't change while it's running, so a changed value takes effect only after a restart.

This is what makes it possible to take one artifact — the executable the build produced — and put that same copy on the test server and the production server. The thing that was tested and the thing that reaches customers are identical, which is the real prize here. Build separately per environment and you leave room for something other than what passed testing to ship.

Names are written in capitals with underscores. The DATABASE_URL convention is settled enough that a lowercase name gets overlooked by the next person as something other than configuration.

Values are always strings. Put in the number 3000 and the program receives the characters 3000, so the code has to convert it. The same is true for true and false, which is why the text "false" being read as true is such a common bug.

Values that must not leak travel the same path but live somewhere else. That practice has its own name, secret management: keep them in a vault-like tool and pull them out only at the moment of execution. Settings screens hide them after the first save for the same reason.

Where it shows up

The most common use is the address of an outside service. Payments, email delivery, file storage — each one has a test address and a real address, and the environment variable makes the choice. While the app points at the test payment service, entering a card number moves no money. Get one address wrong and a test charge made during development appears on a customer's statement.

It also works as an on/off switch. Showing an unreleased screen only in the development environment takes one differing value. Once the switch needs to vary per user, though, an environment variable isn't enough and a feature flag takes over.

Inside an automated deploy, the pipeline is what hands these values out. The same code runs against test values during the checking stages and against production values in the deploy stage, which means one place ends up owning which value goes to which stage.

The list of required names matters for new hires too. A program started with a value missing tends to die without explaining why, so teams keep an example file in the repository listing the names with the values blank. Someone joining copies that file and fills in their own.

How it sounds at work

"It works locally but dies on deploy. Feels like a missing environment variable."

Check the server-side configuration before suspecting the code.

"Don't put that key in the code — pull it out into an environment variable."

A request to keep secrets out of the repository.

"I changed the value and nothing happened. Did you restart it?"

A reminder that these are read once, at startup.

At Pagedeck, Theo once spent half a day with a new engineer whose document preview feature failed only on their machine, with no visible reason. Putting the example file next to the new engineer's own configuration side by side, Theo found one name missing. It had been added two months earlier and never copied into the example file.

After that, Pagedeck added a line to its code review checklist: any change introducing a new environment variable has to update the example file too. A single missing value had swallowed a new hire's first day three or four times before that.

What people get wrong

People often read an environment variable as a place where a secret is safe. Taking it out of the code feels like securing it. But these values sit where any running process can read them, which means anyone who can get onto the server — and any other program running there — sees them plainly. Code that dumps the whole configuration into a log on error will write the key into a log file, and log files usually have several readers. This is better than committing the key, not the same as putting it in a vault.

Collecting values in a single file comes with a second job: making sure that file never reaches the repository. The filename starts with a dot and is easy to overlook, so it gets committed by accident often. Once committed, deleting it later doesn't help — it remains in the history, and the key itself has to be reissued. The cost of the mistake includes whatever stops working while the replacement key is being issued.

Easily confused with

TermWhat it refers toHow it differs
EnvironmentA distinct place to run things — development, staging, productionAn environment is the place; an environment variable is a value plugged into it. Three environments means the same variable name exists three times with three values

Where to go next

  • CI/CD

    see the flow these values get injected into

  • Pipeline

    how different values reach different stages

Last updated 2026-09-16