Sooner or later, your code will need a secret. An API key to talk to OpenAI or Stripe. A password for a database. A token to publish to a service. The wrong way to handle these is the obvious way: type them directly into your code. The right way involves a few small habits that protect you from a class of problems that has embarrassed even very experienced engineers.
Why you don't hardcode secrets
Imagine you write a tiny script that uses an OpenAI API key:
# DON'T do this api_key = "sk-proj-1234567890abcdefghij" client = OpenAI(api_key=api_key)
Then you commit your code to git, push it to GitHub, and your repository is public. Within minutes, automated bots scanning GitHub for leaked keys find yours, copy it, and use it. By morning you have a thousand-dollar bill for OpenAI calls you didn't make.
This is not hypothetical. It happens to people every day.
Environment variables: the standard solution
The operating system has a built-in concept called environment variables. They're named values that live in your shell's environment and get handed to any program you run. Programs ask the OS "hey, what's the value of OPENAI_API_KEY?" and the OS hands it over.
You can set one temporarily from the terminal:
# Mac / Linux export OPENAI_API_KEY="sk-proj-..." # Windows PowerShell $env:OPENAI_API_KEY = "sk-proj-..."
And then your code reads it from the environment:
import os # DO this api_key = os.environ["OPENAI_API_KEY"] client = OpenAI(api_key=api_key)
Now the key never appears in your code. Your code can be public. The key stays on your machine.
The .env file pattern
Typing export commands every time you open a terminal gets old. The community convention is a file called .env — a plain text file at the root of your project, oneKEY=value per line:
OPENAI_API_KEY=sk-proj-1234567890 DATABASE_URL=postgres://user:pass@localhost/db STRIPE_SECRET=sk_live_abcdef
A small library in your project (dotenv in Node and Python; built-in to many frameworks) reads .envwhen your program starts and loads each line into the environment as if you'd typed export yourself.
The most important rule
.env must never be committed to git. Ever. The whole point is that it stays on your machine. The way you guarantee this is by adding .env to your .gitignorefile — the list of things git agrees to pretend don't exist.
# .gitignore node_modules .env .env.local *.log
Most starter projects already have .env in .gitignore by default. Always check.
.env.examplefile that's committed to git, listing the variable names with placeholder values. New contributors copy it to .env and fill in their own real values. You get the documentation benefit without the leak risk.What if I already committed a secret?
First: assume it's compromised. Even if you delete the file and push the deletion, the secret is in your git history forever, and almost certainly already scraped by a bot.
The recovery:
- Rotate the key. Go to the service that issued it (OpenAI, Stripe, etc.) and revoke the leaked one, generate a new one.
- Put the new one in
.envand add.envto.gitignoreif it wasn't already. - Optional but cleaner: rewrite git history to remove the secret entirely (tools like
git-filter-repoor BFG do this). Worth it if the repo is public.
The rotation step is the only one that matters for safety. Don't skip it.
Where Claude Code fits
When Claude writes code that needs an API key, it should always write code that reads from process.env (Node) or os.environ (Python), and then prompt you to put the key in .envyourself. If you ever see Claude pasting a real key into source code, stop it — that's the wrong pattern, even from an AI.
For Claude Code's own configuration (custom settings, MCP servers, etc.), the same logic applies: secrets go in environment variables or local-only config files, never in anything that ends up in your repo.
- Never put secrets directly in code that goes to git. Bots scrape public repos within minutes.
- Use environment variables. Read them in code with
os.environ(Python) orprocess.env(Node). - Use a
.envfile for convenience. Add it to.gitignore. Share a.env.examplewith placeholders. - If you ever leak a key, rotate it immediately. Cleaning git history is secondary.