Five hundred fake orders
A developer typed the live database's address into the shop's code, just to try something.
Later she ran the tests on her laptop. They wrote 500 fake orders into the real shop. The warehouse started packing them.
The code was fine. The mistake was where a setting lived.
By the end of this lesson
Note
I can keep settings out of the code.
Key words
| Word | What it means | Example |
|---|---|---|
| Configuration | the settings an app reads when it starts | |
| Hard-coded | typed into the code itself | |
| Environment | one place the app runs: development, test or live | |
| Environment variable | a named setting given to a program from outside | SHOP_PORT=8080 |
| Default | the value used when a setting is not given | |
| Secret | a setting nobody else may see, such as a password |
The cells students fill in are left blank in this preview.
The code stays the same, the settings change
The shop runs in three environments: a laptop, a test server and live. The code is the same in all three.
What differs is the configuration: which database, which port, which mail server.
So the settings stay out of the code. Each environment gives its own, often as environment variables.
One app, three sets of settings
| Setting | Laptop | Test server | Live |
|---|---|---|---|
| SHOP_PORT | 8000 | 8080 | 443 |
| DATABASE_URL | postgres://localhost/shop | postgres://db.test.internal/shop | postgres://db.live.internal/shop |
| SEND_EMAILS | no | no | yes |
The cells students fill in are left blank in this preview.
Read settings from the environment
# The same two lines of code, with settings from outside
export SHOP_PORT=8080
export DATABASE_URL=postgres://db.test.internal/shop
port="${SHOP_PORT:-8000}" # use 8000 when SHOP_PORT is not set
echo "Shop starting on port $port"
echo "Using database $DATABASE_URL"
# Delete the line "export SHOP_PORT=8080", then run again. Which port is used?
A common mistake
Warning
Many people think a password in the code is safe if the repository is private. It is not. Everyone who can read the code can read it, and it stays in the Git history for ever. Copies of the code travel to laptops and backups, so a secret comes from the environment instead.
Secrets never go in the code
A secret is a setting like a password or a payment key. Leaked, it lets a stranger act as your company.
Teams keep secrets in a secret store. When the app starts, the platform hands it the secret as an environment variable.
Below, find the file that breaks this rule.
Find the typed-in key
mkdir -p app && cd app
# Two files from the payments service
printf 'PAYMENTS_KEY = "pk-4471"\nprint("taking payment")\n' > pay.py
printf 'import os\nPAYMENTS_KEY = os.environ["PAYMENTS_KEY"]\n' > refund.py
# Which file has the key typed into it?
grep -n 'KEY = "' *.py
# Which file reads the key from the environment?
grep -n "os.environ" *.py
A default, or a stop at the start
Some settings have a sensible default. If nobody sets the port, 8000 will do.
Others have no safe default. Guessing a database address is how the fake orders happened.
For those, the app should refuse to start and say which setting is missing. Run it and see both cases.
A setting the shop cannot start without
start_shop() {
port="${SHOP_PORT:-8000}"
db="${DATABASE_URL:?is not set, so the shop will not start}"
echo "Shop starting on port $port with database $db"
}
unset SHOP_PORT DATABASE_URL
export DATABASE_URL=postgres://db.test.internal/shop
( start_shop )
unset DATABASE_URL
( start_shop ) || echo "Start-up refused: fix the setting first"
# Set SHOP_PORT=9000 before the first start, then run again.