Hard-coding configuration is bad. This is one of the things that newbie programmers learn quickly after they realize that have committed sensitive information to a public GitHub repository. And it happens so easily. Often, you are in the spur of the moment, wanting to test something locally, when you figure out that you need some API key in order for your app to work. Adding the key to the code takes a second, and is just as easy to forget when committing. The Twelve-Factor-App methodology calls this a violation, and has whole section on why should be externalized and read from the environment at runtime.
I used to avoid using environment variables, because they made it hard to set up a local environment for testing. I’d either pass all the assignments at the start, or use a cumbersome startup script to export them to the current shell.
Luckily, it is easy to improve the quality of your code, and ease testing in different environments at the same time. It has become somewhat of a standard practice to use .env
 (a.k.a dotenv) files for configuring sensitive data while testing across environments. A dotenv file contains nothing more than text, where  it has one environment variable assignment per line:
ABC_KEY=1234xyz
DEF_KEY=2349875
DEBUG=True
NOTE: Dotenv files must be ignored from version control for exactly the same reason mentioned at the beginning of this post: exposing sensitive information as part of the project is bad. These files are only used to quickly set up or change an environment when needed.
Using a dotenv file in our app #
How do we get our Go app to read a .env
 file? godotenv is a great Go package that does exactly that. It is one of the libraries I include in pretty much all of my Go projects. godotenv will search for a .env
 as part of the project, and if it finds one, will expose the variables in it to the app. All you need to do, is
go get github.com/joho/godotenv
and add the following couple of lines at the start of your project:
package main
import (
"github.com/joho/godotenv"
"log"
"os"
)
func main() {
err := godotenv.Load()
if err != nil {
log.Fatal("Error loading .env file")
}
s3Bucket := os.Getenv("S3_BUCKET")
secretKey := os.Getenv("SECRET_KEY")
// now do something with s3 or whatever
}
If you feel that calling godotenv.Load()
 is too much, you can even auto-load it:
import _ "github.com/joho/godotenv/autoload"
Happy coding!
Links #
Have something to say? Join the discussion below 👇
Want to explore instead? Fly with the time capsule 🛸
You may also find these interesting
The Two Reasons I Prefer Passing Struct Pointers Around
Choosing consistency over performance.
Interfaces Are Not Meant for That
It’s time to ask ourselves how much abstraction in our Go code really makes sense.