The Fallback Layer

An easy Python interview practice problem on DataDriven. Write the Python and run it against test cases, with instant feedback.

Domain
Python
Difficulty
easy
Seniority
L3

Problem

A service builds its runtime config by layering environment values over a baseline. Start from primary, the values read from the environment, and keep every key it has; whenever one of those values is None, fall back to the same key in defaults. A key that lives only in defaults never makes it into the result, and a None with no matching default stays None.

When the live values come back empty, something has to stand in.

Example 1

Input:

primary = {"host":"db.prod","port":null,"timeout":30}, defaults = {"host":"localhost","port":5432,"timeout":10}

Output:

{"host":"db.prod","port":5432,"timeout":30}

Example 2

Input:

primary = {"a":null,"b":null}, defaults = {"a":1,"b":2}

Output:

{"a":1,"b":2}

Worked solution and explanation

What this is really testing

Strip the config-loader costume and this is a per-key null-coalesce, not a dictionary merge. The keys that survive are decided entirely by primary; defaults is only a bench of replacement values you reach for when primary handed you a None. Almost everyone's hand reaches for {**defaults, **primary} on reflex, and that is the trap: it drags in every key that lived only in defaults, and it happily keeps the None values you were supposed to fill. Get that wrong and your service boots with settings the environment never set.


Walking through it

Step 1: Iterate primary, not defaults

The output's key set is exactly primary's key set, so loop over primary.items() and let that drive what lands in the result. Nothing in defaults gets to introduce a new key.

Step 2: Fall back only on a real None

For each value, keep it as-is unless it is exactly None and defaults carries the same key. Use is None, not a truthiness check, or you will treat 0, '', and False as holes and overwrite perfectly good values. A None with no matching default simply stays None.


The solution

Per-key coalesce driven by primary
def coalesce(primary, defaults):
    result = {}
    for key, value in primary.items():
        if value is None and key in defaults:
            result[key] = defaults[key]
        else:
            result[key] = value
    return result
The reflex: dict-unpack merge
{**defaults, **primary} returns the union of both key sets and preserves every None in primary. Wrong key set, wrong values.
Per-key coalesce
Iterate primary, swap in a default only where the value is None and the key exists in defaults. Right key set, holes filled.

Common follow-up questions