DEV Community

Dakota Wu
Dakota Wu

Posted on

Record One Nested Decision, Then Extract a Single Predicate

A pricing function still folds region rules, bulk surcharges, and coupon stacking into one nested block. A teammate asks an assistant to tidy that module before a tax change lands next week. The first generated patch rewrites four helpers, renames two exceptions, and flips a surcharge for twelve-item carts. Review then spends more time reconstructing prior behavior than evaluating the one extract that was actually needed.

This walkthrough treats that failure as a process problem rather than a taste debate about clean code. The useful unit of work is one nested decision on one hot path, recorded before any symbol moves. After the outcomes are frozen, the only permitted edit is a single predicate extract that preserves those outcomes. The fixture below is labeled as an unexecuted example; adapt the recorder to your language and runner.

Why broad cleanup patches fail on nested pricing logic

Messy pricing code is usually a decision tree that hides inside mutation, logging, and ad-hoc rounding. Assistants trained to improve readability optimize for local style, not for the sparse matrix of inputs that production actually hits. A four-hundred-line rewrite can look coherent in diff view while changing only one compound condition that finance already depends on.

Three failure patterns show up repeatedly in review, even when the generated code is syntactically nicer:

  • Silent branch collapse. Two region checks get merged, and a cart that used to skip surcharge now pays it.
  • Exception reshaping. A ValueError becomes a custom type, and an upstream retry path stops matching.
  • Rounding drift. Intermediate round(..., 2) calls move, so a twelve-item cart differs by one cent.

None of those failures are visible if the first test you add is an assertion against the new design. Characterization has to lock the old outcomes first, before any helper is renamed or moved. Only then does a one-predicate extract become a reviewable change rather than a behavior lottery.

Freeze the nested decision, not the surrounding module

Pick the hottest path through the function, not the whole file, before anyone starts renaming symbols. In this fixture, that path is whether a cart receives a bulk surcharge and which reason code is attached. Coupon stacking and tax remain inside the messy function on purpose, because moving them would expand the blast radius past a single commit.

Define a record as a triple: canonical input, outcome tuple, and a stable hash of that pair. The hash is a review signal, not a cryptographic control, and a mismatch should stop the extract immediately. If two consecutive runs disagree, the path is still too noisy to touch.

A compact recorder for one hot path

The module under test is intentionally awkward. It mutates a dict, appends a log line, and buries the surcharge predicate among unrelated branches.

# pricing.py — unexecuted fixture, not production code
from typing import Any


def price_order(cart: dict[str, Any]) -> dict[str, Any]:
    items = cart.get("items") or []
    region = (cart.get("region") or "US").upper()
    coupon = cart.get("coupon")
    subtotal = sum(
        float(i.get("unit_cents", 0)) * int(i.get("qty", 0)) for i in items
    )
    log = list(cart.get("_log") or [])

    surcharge = 0.0
    reason = "none"
    count = sum(int(i.get("qty", 0)) for i in items)

    if region in {"EU", "UK"} and coupon == "VATZERO":
        reason = "vat_exempt"
    elif count >= 12 and region != "EU":
        surcharge = round(subtotal * 0.04, 2)
        reason = "bulk_surcharge"
        log.append(f"bulk:{count}:{region}")
    elif count >= 12 and region == "EU":
        reason = "eu_bulk_skipped"
        log.append(f"skip_eu:{count}")
    else:
        log.append(f"std:{count}:{region}")

    if coupon == "SAVE10" and reason != "vat_exempt":
        subtotal = round(subtotal * 0.9, 2)

    cart["subtotal"] = subtotal
    cart["surcharge"] = surcharge
    cart["reason"] = reason
    cart["_log"] = log
    cart["total"] = round(subtotal + surcharge, 2)
    return cart
Enter fullscreen mode Exit fullscreen mode

The recorder ignores style and stores only the decision surface planned for extract: item count, region, coupon, reason, surcharge, and total.

# characterize_price_order.py — unexecuted fixture
import hashlib
import json
from copy import deepcopy

from pricing import price_order

CASES = [
    {"items": [{"unit_cents": 199, "qty": 12}], "region": "US", "coupon": None},
    {"items": [{"unit_cents": 199, "qty": 12}], "region": "EU", "coupon": None},
    {"items": [{"unit_cents": 199, "qty": 11}], "region": "US", "coupon": None},
    {"items": [{"unit_cents": 500, "qty": 12}], "region": "UK", "coupon": "VATZERO"},
    {"items": [{"unit_cents": 199, "qty": 12}], "region": "US", "coupon": "SAVE10"},
    {"items": [{"unit_cents": 50, "qty": 0}], "region": "US", "coupon": None},
    {"items": [{"unit_cents": 199, "qty": 12}], "region": "eu", "coupon": None},
]


def outcome(cart):
    result = price_order(deepcopy(cart))
    return {
        "count": sum(int(i.get("qty", 0)) for i in cart.get("items") or []),
        "region": (cart.get("region") or "US").upper(),
        "coupon": cart.get("coupon"),
        "reason": result["reason"],
        "surcharge": result["surcharge"],
        "total": result["total"],
    }


def ledger_hash(rows):
    blob = json.dumps(rows, sort_keys=True, separators=(",", ":")).encode()
    return hashlib.sha256(blob).hexdigest()[:16]


if __name__ == "__main__":
    rows = [outcome(c) for c in CASES]
    print(json.dumps(rows, indent=2, sort_keys=True))
    print("ledger", ledger_hash(rows))
Enter fullscreen mode Exit fullscreen mode

Commands that keep the freeze honest

Run the recorder twice before anyone edits pricing.py, and treat a hash mismatch as a stop sign. Store the printed ledger in the review notes, or as a checked-in JSON snapshot if that habit already exists. The second run must match the first hash; if it does not, shrink the recorded surface before extracting anything.

python characterize_price_order.py | tee /tmp/ledger1.txt
python characterize_price_order.py | tee /tmp/ledger2.txt
diff -u /tmp/ledger1.txt /tmp/ledger2.txt
python -m pytest tests/test_price_order_characterization.py -q
git add characterize_price_order.py tests/test_price_order_characterization.py
git commit -m "Characterize bulk-surcharge decision before any extract"
Enter fullscreen mode Exit fullscreen mode

A minimal pytest pin is enough for CI. It should fail on reason-code drift, surcharge drift, or total drift, and it should ignore log-line wording.

# tests/test_price_order_characterization.py — unexecuted fixture
from characterize_price_order import CASES, ledger_hash, outcome

# Captured from the first honest run of characterize_price_order.py
PINNED_HASH = "replace_me_after_first_run"


def test_bulk_surcharge_decision_is_frozen():
    rows = [outcome(c) for c in CASES]
    assert ledger_hash(rows) == PINNED_HASH
Enter fullscreen mode Exit fullscreen mode

Replace PINNED_HASH with the value from the first run, then keep that commit separate from the extract commit. Mixing the pin and the extract in one diff reintroduces the original review problem, because reviewers cannot tell a captured baseline from a behavior change.

A decision table the extract must not violate

The table is the contract for the extract commit. If an assistant proposes a prettier predicate that disagrees with any row, the extract is rejected, regardless of naming quality.

qty region coupon reason surcharge rule
12 US none bulk_surcharge 4% of pre-coupon subtotal
12 EU none eu_bulk_skipped 0
11 US none none 0
12 UK VATZERO vat_exempt 0
12 US SAVE10 bulk_surcharge 4% first; coupon then cuts subtotal
0 US none none 0
12 eu none eu_bulk_skipped 0, because region is uppercased

The SAVE10 row is the interesting collision in this fixture. The current function applies bulk surcharge against the pre-coupon subtotal, then discounts the subtotal. A cleanup that computes surcharge after the coupon looks cleaner and is wrong relative to today's ledger. Characterization exists to make that disagreement boring and automatic, instead of a late finance incident.

The smallest safe change: one predicate, one commit

After the hash is pinned, the only allowed production edit is extracting the condition that decides bulk_surcharge versus eu_bulk_skipped. Coupon handling, logging, and totals stay in price_order during this commit. VAT exemption stays inline as well, because it is a different decision and deserves a later extract.

def bulk_surcharge_reason(count: int, region: str) -> str | None:
    """Return a bulk-related reason, or None when the bulk branch does not apply."""
    if count < 12:
        return None
    if region == "EU":
        return "eu_bulk_skipped"
    return "bulk_surcharge"
Enter fullscreen mode Exit fullscreen mode

Wire it in with the smallest possible splice. Do not reorder coupon math in the same commit, even if the new order reads more linearly.

    bulk_reason = bulk_surcharge_reason(count, region)
    if region in {"EU", "UK"} and coupon == "VATZERO":
        reason = "vat_exempt"
    elif bulk_reason == "bulk_surcharge":
        surcharge = round(subtotal * 0.04, 2)
        reason = bulk_reason
        log.append(f"bulk:{count}:{region}")
    elif bulk_reason == "eu_bulk_skipped":
        reason = bulk_reason
        log.append(f"skip_eu:{count}")
    else:
        log.append(f"std:{count}:{region}")
Enter fullscreen mode Exit fullscreen mode

Then re-run the recorder and the pinned test before opening the review. If PINNED_HASH still matches, the extract changed structure without changing the nested decision. If it does not match, revert and shrink the splice; do not fix forward by editing cases.

A patch-size budget you can enforce in review

Use this checklist on the extract commit only, not on the earlier characterization commit:

  1. Production diff stays under roughly forty lines, including the new function.
  2. Exactly one new symbol is introduced, and no existing exception type is renamed.
  3. Coupon, tax, and logging branches are untouched except for the call-site splice.
  4. Characterization hash is unchanged; any hash delta blocks merge.
  5. No formatter-only edits in unrelated functions ride along in the same commit.
git diff --stat HEAD~1
git diff -U0 HEAD~1 -- pricing.py
git diff --name-only HEAD~1
Enter fullscreen mode Exit fullscreen mode

If an assistant or a colleague cannot stay inside that budget, split the work instead of raising the budget. The next extract might be is_vat_exempt(region, coupon), and it gets its own pin if those cases are not already covered. Sequential extracts are slower to write and much cheaper to review than one impressive cleanup.

Where a coding assistant belongs in this sequence

Assistants are useful after the ledger exists, because the task becomes proposing a predicate that preserves these rows, not making the file look clean. They are much less useful as the first author of a module-wide rewrite, which is how the twelve-item surcharge usually flips. Feed the model the decision table and the current function, then reject any patch that also reformats coupon math.

Disclosure: This article was prepared as part of MonkeyCode's product outreach. If the recorder needs a scratch environment instead of a laptop checkout, MonkeyCode's free model access and free server option can draft extra cases and rerun the hash loop without pointing the assistant at production secrets. Accept only a predicate-sized patch that keeps PINNED_HASH stable, and keep the same pytest pin in CI regardless of which editor wrote the function.

The method still holds if pytest runs on a workstation and the predicate is written by hand. The assistant is optional infrastructure around a frozen decision, not a substitute for the freeze.

Limitations

  • Characterization records what the function does today, including bugs that product may later want to change on purpose.
  • A seven-row table does not cover concurrent carts, currency conversion, or coupons that expire mid-request.
  • Hashing JSON rows will churn if logs, timestamps, or unordered keys are included without sort_keys.
  • Extracting a predicate does not improve observability; existing logs still carry production incidents.
  • Assistants can memorize the table and still reorder surcharge math unless the pin is enforced in CI.

Who should skip this approach

Skip it if the change is an intentional price-policy update rather than a structure-only extract. Skip it if the pipeline cannot run even a single-file pytest target on every patch. Skip it for cryptographic, access-control, or tax-engine code that needs a formal spec, not a snapshot of yesterday's behavior. Skip it when the hot path is not identifiable, because freezing a random nested if teaches the team the wrong boundary.

The durable habit is small and slightly boring. Record one nested decision until its hash is dull, then move one predicate, then stop. The cleanup still happens; it happens as a sequence of reviewable extracts instead of one impressive diff that finance cannot reconstruct.

Top comments (0)