Skip to main content
← All posts
6 min read

Secrets Management Is a DevEx Problem

Engineers spend hours getting secrets to work locally. Every out-of-date .env.example is lost velocity. Secrets aren't just a security concern — they're a developer experience bottleneck.

Share

Every engineer on your team has a .env file that's mostly cargo-culted from someone else's Slack message. Half the values are wrong. Two are obsolete. Three are missing. And nobody updated .env.example when the service moved to a new auth provider six months ago.

Secrets management is discussed almost entirely as a security problem. Rotate your keys. Don't commit credentials. Use a vault. All correct. But there's a parallel problem that's almost completely ignored: the DevEx cost of a broken secret configuration.

The invisible friction

Here's what happens when secrets management is bad:

A new engineer sets up locally. They copy .env.example to .env. They run the app. It crashes. The error says STRIPE_SECRET_KEY is undefined. They search Slack. Someone six months ago shared their own key in a DM that the new engineer wasn't part of. They ask in the engineering channel. Someone responds an hour later: "oh you also need STRIPE_WEBHOOK_SECRET, ask [senior engineer] for the dev values."

Senior engineer is in a meeting. Two hours pass. The new engineer works on something else. Context is lost.

This is a Tuesday for a lot of teams. Multiply by every service. Multiply by every engineer who switches machines or sets up a new dev environment. Multiply by 250 working days.

What makes this hard

Secrets can't go in the repo. That's the right call, obviously. But teams stop there, as if the security constraint explains away the DevEx problem. "We can't check them in" becomes an excuse for having no other solution.

Dev secrets and prod secrets are treated identically. Production secrets need to be tightly controlled. Development secrets for a test Stripe account, a local database, and a dummy SendGrid key do not. But most teams apply the same process — "ask someone who has it" — to both. This scales poorly.

.env.example is aspirational, not operational. Created once, forgotten immediately. New env vars get added to the code, added to CI, but not to .env.example. By the time someone checks, it's a historical artifact.

Nobody knows which secrets are still in use. Services get deprecated. Third-party integrations change. But the .env.example never shrinks — it only grows. Engineers spend time tracking down values for services the app doesn't even use anymore.

The security-DX tradeoff is false

Teams talk about secrets management like security and developer experience are in tension. They're not. The practices that make secrets more secure — centralized storage, auditing, rotation tooling — also make them easier to manage for developers.

The problem isn't the security requirements. The problem is implementing those requirements in a way that makes the developer path worse.

Bad: "All secrets are stored in a shared Notion doc protected by a password." Good: "Run make secrets and your .env is populated from the team vault."

Same outcome for the engineer (working local environment). Wildly different security properties and wildly different DX.

What good looks like

A secrets manager that developers actually use. 1Password Teams, Doppler, Vault, AWS Secrets Manager — pick one. The key requirement: developers can pull down the full set of local dev secrets in one command without asking anyone.

doppler run -- npm run dev or op run --env-file=".env.1password" -- npm start — the exact implementation doesn't matter. What matters is that a new engineer on day one can get a working .env without a synchronous human interaction.

Separate dev secrets from prod secrets. Dev secrets are for a test environment. They rotate less often, have lower blast radius, and can be shared more freely with the team. Prod secrets have strict access controls and audit logs. Model them separately in your secrets manager.

A .env.example that's generated, not maintained. If your secrets manager knows what keys exist, it can generate the example file. If you're on Doppler, for instance, you can generate .env.example from your dev config with secrets redacted. The example stays in sync automatically.

Every env var has a comment explaining what it's for and where to get it. Not in a separate doc — in .env.example itself. STRIPE_SECRET_KEY should have a note: "Stripe test secret key. Get from Stripe dashboard under test mode API keys." When you set up, you don't need to ask anyone.

CI validates the required vars. A startup check that lists every missing required env var, not just crashes on the first one found. Engineers see the full list of what's missing and can address everything at once.

The rotation problem

Secrets need to rotate. When they do, every engineer's local .env is out of date. If your rotation process is "send a Slack message and ask everyone to update manually," you will have engineers running with stale credentials for weeks.

If your secrets are in a vault and developers pull from the vault at startup (or at least on-demand), rotation is invisible. The next time someone runs make secrets, they get the new value. No coordination required.

This is the compounding argument for centralized secret management: not just that setup is easier, but that maintenance is automatic.

What to do if you're starting from scratch

Week 1: audit what you have. List every env var in .env.example. For each one: is it still used? Is it documented? Do you know where to get the value? Is there a dev-safe version?

Week 2: pick a secrets manager and migrate dev secrets. Start with your development environment — lower stakes, immediately valuable. Get to a point where a new engineer can run one command to get their .env populated.

Week 3: fix .env.example. Remove obsolete vars. Add comments to every remaining var. Add any vars that are missing. Have a new engineer or intern validate it from scratch.

Ongoing: own the list. Any new env var added to the codebase must be added to the secrets manager and to .env.example on the same PR. Make it a PR review requirement.

The non-obvious benefit

When secrets management is smooth, teams stop workarounding it. Engineers stop sharing credentials in DMs. They stop having personal dev environments that use prod keys "just for testing." They stop keeping secrets in text files or browser history.

The security posture improves not because you tightened controls, but because the compliant path is the easy path.

That's the goal. Not "secrets are secure despite developer friction" but "secrets are secure because we made the secure way the easiest way." DX and security aren't in tension — bad implementation puts them in tension. Good implementation aligns them.

Fix your .env.example. Pick a vault. Automate the pull. Your engineers will be faster, your secrets will be safer, and you'll stop losing hours to "who has the dev API key for Twilio."

Work with me

I consult with engineering teams on AI adoption, cloud architecture, and engineering effectiveness. If this post surfaced a challenge you're facing, let's talk.

Get in touch →

Explore more on these topics:

Subscribe to new posts

Get an email when I publish something new. No spam, unsubscribe any time.