How to Diff Two Dicts in Python for Config Drift

Recursively compare two dictionaries and report added, removed, and changed keys with their old and new values for debugging configuration drift.

Medium Python 3.9+ Aug 9, 2026 Errors & debugging 13 views 0 copies

Python code

32 lines
Python 3.9+
def diff_dicts(a, b, path=""):
    differences = []

    for key in a.keys() | b.keys():
        new_path = f"{path}.{key}" if path else key

        if key not in a:
            differences.append((new_path, "<missing>", b[key], "added"))
        elif key not in b:
            differences.append((new_path, a[key], "<missing>", "removed"))
        elif isinstance(a[key], dict) and isinstance(b[key], dict):
            differences.extend(diff_dicts(a[key], b[key], new_path))
        elif a[key] != b[key]:
            differences.append((new_path, a[key], b[key], "changed"))

    return differences


if __name__ == "__main__":
    config1 = {
        "server": {"host": "localhost", "port": 8080, "timeout": 30},
        "debug": True,
        "name": "app",
    }
    config2 = {
        "server": {"host": "localhost", "port": 9090, "max_conns": 100},
        "debug": False,
        "version": "2.0",
    }

    for path, old, new, change_type in diff_dicts(config1, config2):
        print(f"{change_type.upper()}: {path} = {old!r} -> {new!r}")

Output

stdout
CHANGED: server.port = 8080 -> 9090
REMOVED: server.timeout = 30 -> <missing>
ADDED: server.max_conns = <missing> -> 100
CHANGED: debug = True -> False
REMOVED: name = 'app' -> <missing>
ADDED: version = <missing> -> '2.0'

How it works

The function recursively walks both dictionaries by taking the union of keys from the two inputs. For each key, it checks if the key is only in one dict (added or removed), or if both values are dictionaries it recurses deeper, otherwise it compares scalar values and records a change. The path parameter builds a dotted key path like server.port so differences are easy to locate. Because the iteration order follows the set union, results may appear in a different order but will always cover every difference.

Common mistakes

  • Forgetting to recurse when both values are dicts, so nested differences are missed.
  • Assuming keys in both dicts are identical, so changes inside nested dicts are overlooked.
  • Using `a.keys() | b.keys()` on older Python versions that don't support dict union or set-like views (needs 3.9+).
  • Not accounting for differences in type (e.g., list vs dict) which the function treats as a simple change.

Variations

  1. Use the `deepdiff` third-party package (pip install deepdiff) to get richer diff output with ignore-order options.
  2. Return a dict of change types instead of a list of tuples, or use a dataclass for typed diff records.

Real-world use cases

  • Compare configuration files across environments (dev vs prod) to spot drift before deployment.
  • Debug why an application behaves differently by comparing runtime settings from two service instances.
  • Check for unexpected changes in a schema or feature flag state when rolling out a new release.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Errors & debugging

Related tutorials and quizzes for this topic.