2 AM, a buzzing phone, and a lesson I’ll never forget
I received an alert from the monitoring system: Production server is down. Checking logs on Grafana, the error was crystal clear: ConnectionTimeout: Cannot connect to database at 192.168.1.50:5432.
It turned out the infrastructure team had just changed the IP range for the database cluster. Because I had accidentally hardcoded the old IP address deep within the code, the system immediately went paralyzed. I had to sift through over 20 Python files, manually edit each line, and rebuild the Docker image in the middle of the night.
After that incident, I established an ironclad rule: Never leave parameters like IPs, Ports, or API Keys directly in the code. Everything must be moved to a Config File. If you want to sleep soundly, let’s master YAML, TOML, and INI together.
Why separate configuration?
Separating configuration from the source code doesn’t just make the code cleaner. It is a vital factor for scaling your system.
- Instant changes: Changing the database port from 5432 to 6432? Just edit one line in a text file; no need to rebuild the code.
- Security: Add configuration files to
.gitignoreto avoid leaking Secret Keys or Tokens on GitHub. - Multi-environment: Run scripts on your local machine (Dev) or Server (Prod) simply by pointing to the corresponding config file.
In the Python ecosystem, we usually revolve around the trio: INI (classic), YAML (DevOps standard), and TOML (modern).
1. INI – Simple and no installation required
The INI format is extremely popular because Python supports the configparser library out of the box. You don’t need to pip install anything extra.
Example config.ini file:
[database]
host = 192.168.1.100
port = 5432
[server]
debug = true
How to handle it with Python:
import configparser
config = configparser.ConfigParser()
config.read('config.ini')
# Read values and automatically convert data types
db_host = config['database']['host']
db_port = config.getint('database', 'port')
is_debug = config.getboolean('server', 'debug')
print(f"Connecting to {db_host}:{db_port}")
Weakness: INI is only suitable for flat configurations. If you have complex nested lists, INI becomes very messy.
2. YAML – The #1 choice for flexibility
If you work with Docker or Kubernetes, YAML is a must-know language. It supports hierarchical structures exceptionally well and is very readable. To get started, install PyYAML:
pip install pyyaml
A config.yaml file is often used to manage a list of nodes:
database:
nodes:
- {host: "db-1.local", port: 5432}
- {host: "db-2.local", port: 5432}
timeout: 30
Python code to read YAML:
import yaml
with open('config.yaml', 'r') as file:
# Always use SafeLoader to block malicious code execution attacks
config = yaml.load(file, Loader=yaml.SafeLoader)
print(f"Connecting to node: {config['database']['nodes'][0]['host']}")
Pro tip: Never use FullLoader for config files from untrusted sources. Hackers can exploit it to run unauthorized Python code on your machine.
3. TOML – The new standard for the Python world
TOML (Tom’s Obvious, Minimal Language) is gradually replacing YAML in many Python projects due to its clarity. It isn’t sensitive to whitespace like YAML, which helps reduce silly syntax errors.
From Python 3.11, the tomllib library is built-in. For older versions, you need to install tomli.
import tomllib
# TOML requires opening the file in binary mode (rb)
with open("config.toml", "rb") as f:
config = tomllib.load(f)
print(f"Max connections: {config['database']['connection_max']}")
Real-world experience: Which one should you choose?
In actual projects, I usually choose based on these criteria:
- Use INI: When writing small scripts that need to run immediately on any machine without installing external libraries.
- Use YAML: When working with large systems, CI/CD, or when you need multi-layered nested structures.
- Use TOML: When you want to follow modern standards (like
pyproject.toml) and prioritize clarity.
Another trick I use is wrapping the config file in a Settings class. Instead of using config['db']['port'], I call settings.DB_PORT. This allows the IDE to provide code suggestions (IntelliSense) and prevents typos in key names.
Good configuration management is the first step to becoming a Senior Developer. Try applying it to your current project, and you’ll find system maintenance much easier.

