Environment Variables Are Not a Secrets Management Strategy
Most codebases have secrets in three places the team knows about and seven places they have forgotten. The three known places are the .env file in development, the CI/CD environment variable configuration, and the production environment on whatever hosting platform the team uses. The seven forgotten places are the git commit history, the Docker build cache, the .env.example file that somehow contains real credentials, the developer laptop where someone ran the production database connection string to debug something in 2023, the backup of the production database sitting in an S3 bucket accessible to everyone in the AWS account, the Slack message where someone posted a connection string asking why it wasn't working, and the internal wiki page that got migrated three times and nobody cleaned up.
Secret management starts with acknowledging that secrets sprawl. The question is not whether you have secrets in wrong places. The question is how many of them are exploitable and how quickly you can find them.
The Problem With "Better Than Hardcoding"
Environment variables became the standard because they are better than hardcoding credentials directly in source files. That is a low bar. Environment variables visible to a process can be read by any other process running as the same user, dumped from container runtime logs, captured by application performance monitoring tools that log environment context, exported in error reports, and inspected by developers with production access who were never supposed to have credential visibility.
"Better than hardcoding" became "good enough," which became "this is how we do secrets." Many organizations running this approach have no rotation schedule, no audit log of who accessed which credentials, no way to determine whether a credential was used by a legitimate process or an attacker, and no mechanism to distinguish between the ten services that all share the same database password because copying it was easier than issuing separate credentials.
What Proper Secrets Architecture Actually Requires
A vault is not a solution on its own. It is an infrastructure component that supports a solution, and dropping HashiCorp Vault or AWS Secrets Manager into an environment does not fix the underlying problem unless the architecture also addresses how secrets get into applications, how they rotate, who can access what, and what happens to the thousands of places the old secrets currently live.
Dynamic credentials are the piece that most implementations miss. A database that issues a unique credential to each application instance, with automatic expiry, means that a leaked credential has a short useful lifetime. A credential that was never written down, never stored, and never persisted anywhere has a much smaller attack surface than one that has been in the same .env file for three years. Vault's database secrets engine does this. AWS IAM roles with temporary credentials via STS do this for AWS resources. Kubernetes workload identity does this for clusters. The pattern exists. Most teams do not reach for it because their existing application code does not support credential refresh, and refactoring all of it is a project nobody has prioritized.
The Hard Part Is Not the Vault
Deploying a secrets management solution is a two to four week project. Migrating secrets out of the places they have accumulated is a six to eighteen month program that nobody wants to fund because it does not ship features.
The migration problem has a few distinct challenges. First, discovery: finding all the places secrets live requires a combination of static analysis tools like TruffleHog or GitGuardian scanning your code and git history, configuration review across your CI/CD systems, and manual audit of documentation. The static analysis tools are fast and will find more than you expect. Git history scans are slow and will find things that have been sitting there since 2019.
Second, triage: not all discovered secrets are current. Some are rotated, some are for decommissioned systems, some are test credentials that were never production. Triage determines what requires immediate rotation versus what is historical noise. Get this wrong in either direction and you either ignore active exposure or spend a week rotating credentials for services that no longer exist.
Third, coordination: the services that consume a secret need to be updated before the secret is rotated. This seems obvious. It creates a dependency graph that organizations routinely underestimate. The shared database password that seventeen microservices use cannot be rotated until all seventeen are updated to read from the vault. Seventeen teams. Seventeen deployment pipelines. Seventeen changes with varying test coverage and rollback postures.
The Audit Trail You Are Missing
One undervalued benefit of centralized secrets management is the access audit log. A well-configured vault logs every credential access: which service requested which secret, when, from which IP, against which version. When you are investigating an incident, "did the attacker have access to database credentials" becomes a question you can actually answer.
Without centralized management, the credential access question is essentially unanswerable. You know what services were configured with the credential. You do not know which processes actually read that environment variable, when, or whether an unauthorized process somehow obtained it. The audit trail that secrets management buys you is not just a compliance checkbox. It is the forensic infrastructure that determines whether an incident was limited to one credential or whether a dozen others were also exposed.
Starting Without a Full Migration
If a full migration is not happening this quarter, the partial steps that buy the most risk reduction are: run a one-time git history scan to find credentials that have ever been committed, rotate everything it finds, and remove them from history or block access to the repository until rotation is complete. Enable MFA for access to your CI/CD secrets store. Stop issuing shared credentials and start issuing per-service credentials even if they are still stored as environment variables. Track when credentials were last rotated and enforce a maximum rotation age.
None of this is as good as a proper vault with dynamic credentials and full audit logging. All of it is better than the credential hygiene that most organizations currently have. The vault can follow, and the migration problem does not shrink if you wait.
The gap between "we have a vault" and "we have a secrets management posture" is mostly organizational and tedious rather than technically hard. That is actually worse: nobody can demo a dependency inventory and a rotation coordination calendar at a board meeting. The work is invisible until a credential shows up in a breach disclosure, at which point everyone wants to know why the team had not prioritized this.