DEV Community

Alex Chen
Alex Chen

Posted on

Learn Configuration Precedence With Empty, Missing, and Invalid Values

Configuration bugs often hide inside one question: does an empty value mean “unset,” “zero,” or “invalid”?

Build a resolver with four layers:

default < config file < environment < command line
Enter fullscreen mode Exit fullscreen mode

Do not use truthiness to choose a value. 0, false, and "" are different inputs with different validation rules.

from dataclasses import dataclass

@dataclass
class Resolved:
    value: int
    source: str

def parse_port(raw, source):
    if raw is None:
        return None
    if raw == "":
        raise ValueError(f"empty port from {source}")
    value = int(raw)
    if not 0 <= value <= 65535:
        raise ValueError(f"invalid port from {source}")
    return Resolved(value, source)
Enter fullscreen mode Exit fullscreen mode

Resolve from highest to lowest precedence, stopping at the first value that is present, then validate it. Do not silently fall through when a high-priority source is malformed; that hides deployment mistakes.

Table-driven tests make the contract visible:

cases = [
    ({}, 8080, "default"),
    ({"PORT": "0"}, 0, "environment"),
    ({"PORT": ""}, ValueError, "environment"),
    ({"PORT": "abc"}, ValueError, "environment"),
]
Enter fullscreen mode Exit fullscreen mode

Add --explain-config so users can inspect the winning source without exposing secrets:

port=8080 source=config-file
host=127.0.0.1 source=default
api_key=<redacted> source=environment
Enter fullscreen mode Exit fullscreen mode

Python’s argparse documentation covers command-line defaults and parsing. The learning goal here is to keep parsing, precedence, validation, and explanation separate so each can be tested.

Extension exercise: add a --no-color boolean and distinguish an omitted flag from an explicit false value. Then serialize the resolved, non-secret settings in a startup test and compare them with an approved snapshot. That small change catches many production configuration mistakes before a framework obscures them.

Top comments (0)