Vault, environment variables, and what not to do.
A few years ago, a startup lost access to their entire AWS account.
Not because someone hacked them. Not because their infrastructure was poorly built.
Because a developer pushed code to GitHub on a Friday afternoon and forgot that the AWS access keys were sitting right there in the file.
Within minutes, an automated bot had scanned the repository, found the credentials, and started spinning up cloud instances to mine cryptocurrency.
By Monday morning — a $7,000 AWS bill. And a very difficult conversation to have.
I wish this was an unusual story. It is not.
It happens quietly, in teams of all sizes, more than the industry likes to admit. And it almost always starts the same way — someone treated secrets like regular code.
Secrets are not regular code. And the way you handle them will either protect your systems or expose everything.
First — What Are Secrets?
Before we talk about how to protect them, let's be clear about what we're actually protecting.
A secret is any piece of information that grants access to something sensitive. That includes:
API keys — credentials that let your application talk to third-party services like Stripe, Twilio, or SendGrid.
Database passwords — the credentials your app uses to connect to your database in production.
Private keys and certificates — used for encryption, authentication, and secure communication between services.
OAuth tokens — tokens that represent user or service identity across systems.
Environment-specific configuration — things like internal service URLs, feature flags tied to billing, or third-party webhook secrets.
If someone gets their hands on any of these, they can read your data, impersonate your services, run up your cloud bill, or take your entire system offline.
The stakes are real. Treat them that way.
What Not To Do — The Mistakes That End Careers
Let me start here because this is where most engineers go wrong. And I want to be specific, because vague advice like "don't hardcode secrets" doesn't actually help you understand why people keep doing it.
Never Hardcode Secrets in Your Code
This is the most common mistake. It looks like this:
python
db_password = "mySecretPassword123"
api_key = "sk_live_abc123xyz"It feels harmless when you're moving fast. You tell yourself you'll clean it up later. You never do.
The moment that code touches a repository — even a private one — that secret is at risk. Private repositories get made public by accident. Teammates clone repos on personal laptops. Old commits stay in git history forever, even after you delete the line.
There is no safe version of hardcoded secrets. None.
Never Commit .env Files to Git
A .env file is a common way to store secrets locally during development. That is fine. Committing it to GitHub is not.
I have seen .env files sitting in public repositories with live database credentials, production API keys, and payment gateway secret keys — all exposed, all valid.
Your .gitignore file should always include .env before you write a single line of code in a new project. Make it a reflex. Not a second thought.
Never Share Secrets Over Slack or Email
This one sounds obvious until you are in a hurry and a teammate asks for the database password to debug something urgently.
The moment a secret lives in a Slack message or an email thread, you have lost control of it. You don't know who screenshots it, who has access to that channel, or how long it will sit in an inbox.
If you need to share a secret with a teammate, use a proper secrets tool — which we will get to shortly.
Never Use the Same Secret Across Multiple Environments
Your development database password should never be the same as your production database password.
If your development environment is compromised — which is far more likely than production — an attacker now has your production credentials too.
Keep environments completely separate. Different secrets, different access levels, different everything.
How Senior Engineers Actually Handle This
Now let's talk about what good looks like. Senior engineers don't just avoid bad habits. They build systems that make bad habits nearly impossible.
Level 1 — Environment Variables Done Right
The first step up from hardcoded secrets is environment variables. Instead of writing the secret in your code, you reference an environment variable:
python
import os
db_password = os.environ.get("DB_PASSWORD")The actual value lives outside the code. On your local machine, it sits in a .env file that never touches git. In production, it is injected by your deployment platform — whether that is a cloud provider, a container orchestration system, or a CI/CD pipeline.
This is the baseline. Every engineer should be doing this. But it is not enough on its own.
Environment variables still have to live somewhere. Someone still has to manage them. And in most teams, they end up in a shared document or passed around in messages — which brings back the same problems.
That is where a dedicated secrets manager comes in.
Level 2 — Using a Secrets Manager
A secrets manager is a tool specifically designed to store, control access to, and audit the use of secrets. It is the standard for any serious production environment.
The most widely used options are:
HashiCorp Vault — the most powerful and flexible option. You can run it yourself or use the managed cloud version. It supports dynamic secrets, meaning it can generate a unique database credential for each application request and automatically expire it after use. That way, even if a credential leaks, it is already invalid.
AWS Secrets Manager — if you are already on AWS, this integrates naturally with your infrastructure. You store secrets in it, and your application retrieves them at runtime using the AWS SDK. Access is controlled through IAM roles, so you never have to pass credentials directly to your application.
GCP Secret Manager and Azure Key Vault — the equivalents on Google Cloud and Azure. Same concept, different ecosystem.
The general pattern is the same across all of them:
- Store the secret in the secrets manager — not in your code, not in your environment, not in a document.
- Give your application an identity — an IAM role, a service account, a Vault policy.
- At runtime, your application uses that identity to fetch the secret it needs.
- The secret never lives in your codebase, your logs, or your deployment configuration.
This is how senior engineers sleep well at night.
Level 3 — Dynamic Secrets
This is where things get genuinely interesting, and it is something most junior and mid-level engineers have never seen in practice.
With static secrets, you create a database password, store it, and your application uses it for months or years. If it leaks, the attacker has long-term access.
With dynamic secrets — which HashiCorp Vault supports natively — the secrets manager generates a brand new, unique credential every time your application needs one. That credential is only valid for a short window of time. When the window expires, the credential dies automatically.
This means there is nothing persistent to steal. Even if someone intercepts a credential, it is already expired before they can use it meaningfully.
You don't need to implement this on day one. But understanding that it exists changes how you think about secrets entirely.
Level 4 — Rotation and Auditing
Senior engineers don't just store secrets safely. They rotate them regularly and audit who accessed what and when.
Rotation means periodically changing secrets — even if nothing has gone wrong. Most secrets managers can automate this. AWS Secrets Manager, for example, can automatically rotate your RDS database password on a schedule without any manual intervention.
Auditing means keeping a log of every time a secret was accessed, by which service, at what time. If something goes wrong, you need to be able to answer: who touched this secret and when? Without an audit trail, incident response becomes guesswork.
Both of these are non-negotiable in a mature production environment.
A Simple Framework to Start With Today
If you are early in your career and all of this feels like a lot, here is a clean starting framework:
Step 1 — Never hardcode. Every secret lives in an environment variable. No exceptions.
Step 2 — Protect your .env files. Add .env to .gitignore before anything else. Use .env.example with placeholder values to show teammates what variables are needed — without exposing the actual values.
Step 3 — Use a secrets manager in production. Start with whatever matches your cloud provider. AWS Secrets Manager if you are on AWS. GCP Secret Manager if you are on Google Cloud. You don't need Vault on day one.
Step 4 — Rotate and audit. Enable automatic rotation where you can. Enable CloudTrail or equivalent audit logging so you always know who accessed what.
Step 5 — Scan your repositories. Tools like GitGuardian and TruffleHog scan your codebase and git history for accidentally committed secrets. Run them regularly. Set them up as part of your CI/CD pipeline so every pull request is checked automatically.
This Week's Challenge
Open one of your current projects — personal or professional.
Search the codebase for any hardcoded passwords, API keys, or tokens. Look for patterns like password =, api_key =, secret =.
If you find any — move them to environment variables today. Not tomorrow. Today.
Check your .gitignore. Is .env in there? If not — add it right now.
One audit. One afternoon. It could save you from a conversation you never want to have.
Final Thoughts
The engineers who never have a secrets incident are not lucky.
They built habits early. They set up systems that make mistakes hard to make. They treated secrets with the same seriousness that a doctor treats patient records — with discipline, with access control, and with accountability.
You don't have to be a security expert to do this well. You just have to care enough to do it right from the start.
One leaked credential can undo months of good engineering. Don't let that be your story.