Use environment variables or a secrets manager, never hardcode credentials in code or config
Hardcoded credentials end up in version control, container images, logs, and error reports. The standard practice is to keep secrets out of the codebase entirely. For local development, read them from environment variables, optionally loaded from a .env file that is gitignored. In production, use a secrets manager such as AWS Secrets Manager, GCP Secret Manager, Azure Key Vault, or HashiCorp Vault, and have the application fetch the secret at startup or on rotation. Inject secrets as environment variables or mount them as files, and never bake them into images. Use a library like python-dotenv only for local development, not as a production secret store. For generated tokens, use the secrets module rather than random. Add pre-commit hooks and secret scanning to CI to catch accidental commits. Rotate credentials regularly and make rotation automated so it is not an outage risk.
Environment variables: simple, works with most platforms, but visible to child processes and in some crash dumps.
Secrets managers: access-controlled, auditable, support rotation. Preferred for production.
Never commit secrets. Use .gitignore for .env and add secret scanning in CI.
Use the secrets module for tokens and passwords generated in code.
Avoid logging secrets. Mask them in logs and error reports.
Trade-off: environment variables are easy but hard to rotate and easy to leak. Secrets managers add a dependency and startup latency but are auditable.
Common mistake: storing secrets in a config file that is checked into version control, even if the repo is private.
Common mistake: using python-dotenv in production, which often means the .env file is deployed alongside the code.
Version note: the secrets module was added in 3.6. There is no version-specific security requirement beyond that.
0-2 years experience
2-5 years experience
5-8 years experience
8+ years experience