Typical environments
- Development - one per engineer
- Testing - checks code works on a machine with known configuration, not just your machine
- Staging - as close to production as similar, but not used by client (if you have to mess up, do it there)
- Production - client facing
Parts that differ between environments
- Database and other services urls
- Paths to resources
- Ports application uses
- Logging options
Options for passing configuration to application
- Command line arguments
- Environment variables
- Configuration file
Command line arguments
Passing configuration to application:
./myapp --database-url=... --port=123 ---loglevel=info
Using configuration inside application:
# .myapp.py
def get_arguments_parser():
parser = argparse.ArgumentParser()
parser.add_argument("--database-url")
parser.add_argument("--port")
parser.add_argument("--loglevel")
return parser
def foo(database_url):
print("Doing something with {}".format(database_url))
def main():
parser = get_arguments_parser()
arguments = parser.parse_args()
foo(arguments["database_url"])
Disadvantages:
• a lot of typing, thus not very convenient
• easy to make a mistake
• doesn't scale
Environment variables:
Passing configuration to application:
DATABASE_URL=... PORT=123 LOGLEVEL=info ./myapp
Suffers from exactly same problems as above
OR
# .bashrc
export DATABASE_URL=...
export PORT="123"
export LOGLEVEL="info"
Then from command line:
source ~/.bashrc
./myapp
Using configuration inside application:
# .myapp.py
import os
def foo():
database_url = os.environ["DATABASE_URL"]
print("Doing something with {}".format(database_url))
def main():
foo()
Disadvantages:
• Configuration isn't stored in project, thus can't be version controlled
• Environment variables are essentially globals, so it's hard to see all inputs to a given piece of code
Configuration file:
Define configuration file:
# ./configurations/development_config.yaml
DATABASE_URL=...
PORT="123"
LOGLEVEL="info"
Passing configuration to application:
./myapp --config-path=./configuration/development_config.yaml
Using configuration inside application:
# .myapp.py
import yaml
def get_arguments_parser():
parser = argparse.ArgumentParser()
parser.add_argument("--config-path")
return parser
def foo(database_url):
print("Doing something with {}".format(database_url))
def main():
parser = get_arguments_parser()
arguments = parser.parse_args()
with open(arguments["config-path"]) as file:
config = yaml.loads(file)
foo(config["database_url"])
Advantages:
• Version controlled
• Easy to add new options
• Visible in code