Document your secrets
It’s uttermost important to be vigilant about the
secrets in your software project. API keys,
tokens and other secrets should never enter version control (or they will
eventually leak by accident). A common way to store secrets is a combination
of an .env
file that is never checked in
and a .env.sample
file that only
contains the structure of the file with no actual content. When you then start
a new project, you copy .env.sample
to
.env
, fill in the secrets and you’re
good to go.
Well, at least if someone in the project remembers what those secrets were and how you’d get them. Sometimes there’s documentation somewhere: in the readme file, in the internal wiki or on a post-it on the most senior developer’s desk somewhere.
We touch those secrets so rarely that unless we bring in new developers into team every month, we’re bound to start forgetting: what does this key actually mean, where do you get it, is it personally generated or shared by the team and what permission levels it should have in a system that supports permission management.
Even my simple one-person static site webpage project has six different environment variables and if I wouldn’t document them, I most certainly wouldn’t remember in 6 to 12 months where I can get new ones.
Documentation in .env.sample
Here’s an older example section from my
.env.sample
:
# Ghost CMS configuration
GHOST_URL= # The root URL to the domain where the CMS lives
# Ghost's Content API key, you can find it from
# Ghost Admin under Integrations under hamatti.org integration
# (see https://ghost.org/docs/content-api/#key)
GHOST_API_KEY=
When I fetch blog posts from my Ghost CMS to my static site, I need two environment variables: the URL from where my CMS lives and the API key. They are two very different type of values in nature: the URL does not necessarily need to be secret and the knowledge of what the value is, is determined by where I host my CMS – hence it lives in my head (and or documentation). On the other hand, the API key absolutely must be secret as it has power to deal with my CMS contents and it can be (re)generated via Ghost’s admin panel.
If you only have one or two that need generating, you might remember where to find them. But most software projects I’ve been working on have half a dozen at minimum. It becomes vitally important to document as detailed as possible, where to generate these secrets.
There are (at least) three things to document for an environment value:
- What does it do or enable? You may have keys that turn on/off features and some sections or integrations might not be needed in local development environment other than in very specific use cases. Make it easy for developers in your team to know how that is.
- Where to find it or generate it? Being specific here helps a ton. Documenting that something is “generated in AWS” will lead to nothing but a wild goosehunt. Linking to platform’s documentation is a good idea since if something changes in the UI where they are generated, their documentation is hopefully updated too.
- What permissions should be enabled? For integration keys that enable permission management, I find it valuable to specify in documentation what is the minimum set of permissions that should be given to a key. This avoids issues down the line when you realize someone accidentally used a key that has power to do everything it and it caused accidents.
Writing documentation at the right detail is hard. I struggle with it every day but I aim to become better. One thing that often makes me struggle when reading documentation is when something states what something is rather than what it should be in this context.
An example of what I consider really good is this from Outline project:
# Specify what storage system to use. Possible value is one of "s3" or "local".
# For "local", the avatar images and document attachments will be saved on local disk.
FILE_STORAGE=local
I like that it specifies what the possible values are and what the
consequences are. For completeness, it could also state where the images and
attachments are stored when the value is set to “s3”, especially as there are
S3 related environment values in the file as well. But regardless it’s very
good and one of the better comments I’ve seen on
.env.sample
files.
Inspiration
If you want to see some inspiration, I recommend
searching GitHub with query path:**/.env.sample
to see how different projects deal with these files.
There you can find examples of both completely undocumented samples (do a test: how many of them are ones where you can confidently say you know what should go there and where to find it) and some that are nicely documented.