Environment Variables
What it is
Section titled “What it is”An environment variable is a named string the operating system hands to a program at the moment it starts. The program gets a copy. Anything that program launches inherits a copy of that copy.
That is the whole model, and one half of it causes nearly all the confusion:
A running program cannot see changes you make after it started.
Not “usually doesn’t.” Cannot. Setting a variable does not reach backwards into anything already running — not your editor, not your dev server, not the terminal you opened ten minutes ago.
Two things commonly called environment variables that are not. A shell variable
(GREETING=hi in Bash) is visible to your shell and not to programs it launches;
export is what promotes it. A .env file is not known to the operating system
at all — a library inside your app reads it, which is why things outside your app
cannot see those values.
When you’d reach for it
Section titled “When you’d reach for it”A value that is different on your machine than on someone else’s: a database URL, a port, an endpoint that is localhost for you and something else in production.
A secret you do not want in the repository — an API key, a token.
A switch you want to flip without editing code: a log level, a feature toggle.
The common thread is that the code is the same everywhere and the value is not.
How to tell if you should
Section titled “How to tell if you should”One question: would putting this value in git be wrong, or meaningless?
Wrong means it is a secret. Meaningless means it is only true on one machine. Either answer points to an environment variable.
If the value is the same for everybody and stays that way, it is a constant — put it in the code where people can find it.
Then one follow-up, because it decides where you set it: does this need to survive closing the terminal? If no, set it for the session. If yes, set it in your user profile — and expect to open a new terminal before you can see it.
How it goes wrong
Section titled “How it goes wrong”You set it and the running thing never sees it. The dev server was started before you set the variable, so it holds the older environment. It presents as the variable being “not set” in code while your terminal plainly shows it. Restart the process. If you set it permanently, restart the terminal and then the process — and note that a terminal inside your editor inherited from the editor, which inherited from whenever you opened it.
You set it permanently and check it in the same window. You will see the old value, conclude it did not work, and set it again. This is the most common way to lose an afternoon to this topic.
It works in one shell and not the other. PowerShell and Git Bash are separate processes. A session variable set in one was never handed to the other.
Case. Windows treats Path and PATH as the same name. Bash does not, and
neither does anything Unix-shaped. Code that reads API_KEY will not find Api_Key
the first time it runs in CI or a container, and it worked fine locally.
Quoting. export MSG=hello world sets MSG to hello and then tries to run a
command called world.
Treating them as secure. They are not encrypted and not private. Every child
process inherits them, printenv lists them, and CI logs and crash reports print
them cheerfully. Keeping a secret out of your repository is worth a lot; it is not a
vault.
Try it
Section titled “Try it”Two minutes. The point is not the greeting — it is watching a value fail to cross a boundary you did not know was there.
PowerShell. Set it, read it, then have a child process read it:
$env:GREETING = "hello from PowerShell"$env:GREETINGpowershell -NoProfile -Command '"child sees: " + $env:GREETING'Now make it permanent and read it back in the same window:
[Environment]::SetEnvironmentVariable("GREETING", "persisted", "User")$env:GREETINGWhat you should see
The first block prints the greeting twice — the child inherited it, because the parent was already holding it when the child started.
The second block still prints hello from PowerShell, not persisted. You wrote the
new value to your Windows user profile, and this session is still holding the copy it
made when it started. Open a new terminal and it will say persisted.
Git Bash. Run this, then run it again with export in front of the first line:
GREETING="hello from Git Bash"echo "$GREETING"bash -c 'echo "child sees: $GREETING"'What you should see
Without export, echo prints the greeting and the child prints nothing. With
export, both print it.
Without export you have a shell variable — your shell can read it, but it is not
part of the environment handed to programs your shell starts. export is the word
that moves it from my shell knows this to everything I start knows this.
Note that echo worked both times. The check most people reach for is the one that
passes either way.
Now cross them. With GREETING set in your PowerShell session, open Git Bash:
echo "${GREETING:-(not set)}"What you should see
(not set). Same machine, same folder, same project — different process, and session
variables were never shared between them. Only the persisted one will show up, and
only in a window opened after you set it.
To see everything at once: Get-ChildItem Env: in PowerShell, printenv in Git Bash.
Ask about
Section titled “Ask about”Three ways to take this further. Paste them as they are, or edit in your own project’s details — the context is what makes the answer worth having.
Verify the model. “I now think an environment variable is a copy the operating
system hands a program when it starts, which is why a running program never sees later
changes and why export matters in Bash. Is that the whole picture, or is there
something that matters on a real project that I am missing?”
Break it. “Give me a sequence of commands I can actually run where I set an environment variable, verify it correctly in my terminal, and a program still does not see it. Show me each command and what it prints, so I can reproduce it rather than take your word for it.”
Map around it. “I can configure a project with environment variables now. What do teams reach for when environment variables stop being enough, and what problem does each of those actually solve?”
If you remember one thing
Section titled “If you remember one thing”A program gets its environment once, when it starts. Set the value before you start the thing, or the thing will never know.