Did the Config Change?
Part of Day One
This is part of Day One: Python for Platform Engineers.
You deployed. Something's slightly off — not broken enough to trigger alerts, but wrong. The service is responding but behavior has changed. Your first question: is the running configuration actually what you deployed?
diff works on files. But the config you care about might be a JSON response from an API, the output of kubectl get configmap -o json, or a YAML file that's gone through several hands. Python lets you compare the actual data, not the text representation.
The diff Approach and Its Limits
| Comparing config files with diff | |
|---|---|
diff shows you line-by-line text differences. That means a key reordered in a JSON object looks like a change even when the values are identical. A YAML file with different indentation looks different even if it means the same thing. You end up chasing formatting noise instead of real drift.
Python parses the config into data, then compares the data.
The Concept: Structural Comparison
Python's comparison logic ignores formatting and key order, focusing only on the data structure and values.
flowchart TD
A([Load Configs]) --> B[json.load / yaml.safe_load]
B --> C{Compare Keys}
C -->|Key missing| D[Report MISSING]
C -->|Extra key| E[Report UNEXPECTED]
C -->|Keys match| F{Compare Values}
F -->|Dict vs Dict| G[Recurse into nested path]
F -->|Value mismatch| H[Report CHANGED]
F -->|Match| I[Next Key]
G --> C
I --> J([Match Complete])
style A fill:#1a202c,stroke:#cbd5e0,stroke-width:2px,color:#fff
style B fill:#2d3748,stroke:#cbd5e0,stroke-width:2px,color:#fff
style C fill:#4a5568,stroke:#cbd5e0,stroke-width:2px,color:#fff
style D fill:#c53030,stroke:#cbd5e0,stroke-width:2px,color:#fff
style E fill:#c53030,stroke:#cbd5e0,stroke-width:2px,color:#fff
style F fill:#4a5568,stroke:#cbd5e0,stroke-width:2px,color:#fff
style G fill:#2d3748,stroke:#cbd5e0,stroke-width:2px,color:#fff
style H fill:#c53030,stroke:#cbd5e0,stroke-width:2px,color:#fff
style I fill:#2d3748,stroke:#cbd5e0,stroke-width:2px,color:#fff
style J fill:#2f855a,stroke:#cbd5e0,stroke-width:2px,color:#fff
Comparing Two JSON Files
json.load(f)parses the JSON file into Python data — dicts, lists, strings, numbers. Order doesn't matter; the data structure does.set(expected) | set(actual)is the union of keys from both dicts — catches keys in expected but not actual (missing) and keys in actual but not expected (unexpected).- When both values are dicts, recurse into them. The
pathargument builds a dotted key path like"database.connection.timeout"so you know exactly where the difference is.
| Example output | |
|---|---|
That second one — a feature flag gone missing — is exactly the kind of thing diff would bury in a sea of JSON formatting noise.
Comparing YAML Files
The same approach works for YAML. You need the pyyaml package:
| compare_yaml.py | |
|---|---|
yaml.safe_load()notyaml.load().safe_loadrefuses to deserialize arbitrary Python objects, whichyaml.load()will execute. Always usesafe_loadwith files you didn't write yourself.
Once loaded, YAML becomes Python dicts and lists — the same find_differences function works on both.
Comparing Against a Live API Response
Comparing a running service's config against what you expect — without a file on disk — is where this becomes a daily-use tool:
This is the workflow that catches config drift in a running system — not just after a deploy, but on a schedule or as part of a smoke test.
Ignoring Fields You Don't Care About
Some fields change legitimately and you don't want to compare them — timestamps, instance IDs, build hashes:
| Ignoring dynamic fields | |
|---|---|
- Set subtraction removes the ignored keys from comparison. To add another field to ignore, edit the set.
Once you're comparing data instead of text, finding drift stops being a needle-in-haystack problem and starts being a script you run on a schedule.
Practice Exercises
Exercise 1: Compare a Kubernetes ConfigMap
A Kubernetes ConfigMap can be exported with kubectl get configmap myconfig -o json. Write a script that compares the data section of two such exports.
Answer
| Compare ConfigMap data sections | |
|---|---|
.get("data", {})returns thedatafield if it exists, or an empty dict if it doesn't. Safer thancm["data"]which would raise aKeyErrorif the field is missing.
Exercise 2: Exit codes for CI integration
The script currently exits with code 1 if differences are found and 0 if not. Extend it so it exits 2 if it can't read one of the input files (e.g., file not found), distinct from a config mismatch.
Answer
Quick Recap
| Concept | What It Does |
|---|---|
json.load(f) |
Parse JSON file into Python dict/list |
yaml.safe_load(f) |
Parse YAML file into Python dict/list |
set(a) \| set(b) |
Union of keys from both dicts |
set(a) - IGNORE_KEYS |
Remove keys you don't want to compare |
| Recursive function | Walk nested dicts to find deep differences |
resp.json() |
Parse HTTP response body as JSON |
What's Next
- Run This Everywhere — When the config is right but you need to verify the same thing across your whole fleet
Further Reading
Official Documentation
jsonmodule —json.load(),json.loads(),json.dump()pyyamldocumentation —safe_load()and why to use it
Deep Dives
deepdifflibrary — A more complete solution for production config comparison tools, with type-aware comparison and configurable ignore rules
Exploring Kubernetes
- kubectl Commands — Extracting live config with
kubectl get configmap -o jsonandkubectl get deployment -o yaml, which feed directly into the comparison scripts here - Understanding kubectl — What kubectl is actually doing when it talks to the API server