DEV Community

Dakota Huang
Dakota Huang

Posted on

Pin Which Branches Fire Before One Predicate Extract

A tangled function is not ready for extraction. Hidden predicates change priced results after a cleanup. Pin which branches fire for a fixture set.

Then extract one predicate from that function. Stop after that single extract lands green.

Why return-value tests still miss money bugs

A single cents assertion hides extra control-flow paths. Two event lists can share one total. Coupon then holiday differs from holiday then coupon.

The order of if blocks is load-bearing behavior. Deleting an elif is a product change.

Dumping the file into a coding model multiplies that risk. The model simplifies predicates you never measured. Measure every predicate before any code extract.

The pin: a branch-outcome table

Each table row represents exactly one fixture. Each row stores inputs, booleans, events, and cents. Booleans are the predicates the function already computes.

Events are the side-effect tokens it already appends. Cents is the frozen money result for that row.

You treat that JSON file as the golden contract. Any later extract must reproduce every table cell. Extra events fail the test on purpose.

Missing events fail the same test as well. Reordered events fail the test as well.

This table is narrower than a full suite. It covers one function and one extract. That limited scope is the safety mechanism.

Worked example (labeled, not production)

The code below is an example harness. Copy it into a throwaway directory first.

Run it locally before you extract anything. Do not treat the numbers as performance claims. This harness is a labeled example, not executed here.

Step 1 — Isolate the tangled function

Create pricing.py with mixed policy and arithmetic. Name each predicate in a local boolean. Do not extract any helpers in this step.

# pricing.py — example only, not production
from __future__ import annotations

from dataclasses import dataclass
from datetime import date
from typing import Any

HOLIDAYS = {
    "US": {(12, 25), (7, 4)},
    "EU": {(12, 25), (5, 1)},
}


@dataclass(frozen=True)
class Order:
    sku: str
    qty: int
    unit_cents: int
    region: str


@dataclass(frozen=True)
class User:
    tier: str
    coupon: str | None


def apply_price(
    order: Order,
    user: User,
    today: date,
    flags: dict[str, Any],
) -> dict[str, Any]:
    events: list[str] = []
    cents = order.qty * order.unit_cents
    bulk = order.qty >= 10
    vip = user.tier == "vip"
    member = user.tier in {"member", "vip"}
    holiday = (today.month, today.day) in HOLIDAYS.get(order.region, set())
    coupon_ok = user.coupon == "SAVE10" and member
    surge = bool(flags.get("surge")) and order.region == "US"

    if bulk:
        cents = int(cents * 0.9)
        events.append("bulk")
    if vip:
        cents = int(cents * 0.95)
        events.append("vip")
    elif member:
        cents = int(cents * 0.98)
        events.append("member")
    if holiday:
        cents = int(cents * 1.15)
        events.append("holiday")
    if coupon_ok:
        cents -= 1000
        events.append("coupon")
    if surge:
        cents = int(cents * 1.08)
        events.append("surge")
    if cents < 0:
        cents = 0
        events.append("floor")

    return {
        "cents": cents,
        "events": events,
        "predicates": {
            "bulk": bulk,
            "vip": vip,
            "member": member,
            "holiday": holiday,
            "coupon_ok": coupon_ok,
            "surge": surge,
        },
    }
Enter fullscreen mode Exit fullscreen mode

The function returns predicates on purpose for measurement. That extra return key is the measurement seam.

Production code can drop that key later. Keep it while the table is the contract.

Step 2 — Build a fixture grid

Create fixtures.py with rows that flip one predicate. Include one stacked case that fires several rules.

Include a floor case that clamps negative cents. Skip random fuzz during this first table pass.

# fixtures.py — example only
from datetime import date
from pricing import Order, User

ROWS = [
    {
        "id": "guest_small_us",
        "order": Order("sku", 1, 2000, "US"),
        "user": User("guest", None),
        "today": date(2026, 3, 2),
        "flags": {},
    },
    {
        "id": "member_bulk_us",
        "order": Order("sku", 10, 2000, "US"),
        "user": User("member", None),
        "today": date(2026, 3, 2),
        "flags": {},
    },
    {
        "id": "vip_holiday_eu",
        "order": Order("sku", 2, 5000, "EU"),
        "user": User("vip", None),
        "today": date(2026, 12, 25),
        "flags": {},
    },
    {
        "id": "member_coupon",
        "order": Order("sku", 1, 4000, "US"),
        "user": User("member", "SAVE10"),
        "today": date(2026, 3, 2),
        "flags": {},
    },
    {
        "id": "guest_coupon_ignored",
        "order": Order("sku", 1, 4000, "US"),
        "user": User("guest", "SAVE10"),
        "today": date(2026, 3, 2),
        "flags": {},
    },
    {
        "id": "us_surge_july4",
        "order": Order("sku", 1, 1000, "US"),
        "user": User("guest", None),
        "today": date(2026, 7, 4),
        "flags": {"surge": True},
    },
    {
        "id": "coupon_floor",
        "order": Order("sku", 1, 500, "EU"),
        "user": User("member", "SAVE10"),
        "today": date(2026, 1, 1),
        "flags": {},
    },
    {
        "id": "vip_bulk_holiday_surge",
        "order": Order("sku", 12, 2500, "US"),
        "user": User("vip", "SAVE10"),
        "today": date(2026, 12, 25),
        "flags": {"surge": True},
    },
]
Enter fullscreen mode Exit fullscreen mode

Eight rows is enough for this extract. Add a row when a predicate has no True case. Do not add rows for unplanned extracts.

Step 3 — Record the golden table

# record_branches.py — example only
import json
from pathlib import Path

from fixtures import ROWS
from pricing import apply_price


def row_record(row: dict) -> dict:
    out = apply_price(row["order"], row["user"], row["today"], row["flags"])
    return {
        "id": row["id"],
        "cents": out["cents"],
        "events": out["events"],
        "predicates": out["predicates"],
    }


def main() -> None:
    table = [row_record(r) for r in ROWS]
    Path("branch_table.golden.json").write_text(
        json.dumps(table, indent=2, sort_keys=True) + "\n",
        encoding="utf-8",
    )


if __name__ == "__main__":
    main()
Enter fullscreen mode Exit fullscreen mode

Record the golden table once before the extract. Never regenerate it to make tests pass.

python record_branches.py
Enter fullscreen mode Exit fullscreen mode

Commit branch_table.golden.json in the same change set. Review the booleans as if they were production data. A False holiday on 25 December is a fixture bug.

Step 4 — Lock the table in a test

# test_pricing_table.py — example only
import json
from pathlib import Path

from fixtures import ROWS
from pricing import apply_price

GOLDEN = json.loads(Path("branch_table.golden.json").read_text(encoding="utf-8"))


def test_branch_table_matches_golden() -> None:
    got = []
    for row in ROWS:
        out = apply_price(row["order"], row["user"], row["today"], row["flags"])
        got.append(
            {
                "id": row["id"],
                "cents": out["cents"],
                "events": out["events"],
                "predicates": out["predicates"],
            }
        )
    assert got == GOLDEN


def test_event_order_is_part_of_contract() -> None:
    stacked = next(r for r in GOLDEN if r["id"] == "vip_bulk_holiday_surge")
    assert stacked["events"] == [
        "bulk",
        "vip",
        "holiday",
        "coupon",
        "surge",
    ]
Enter fullscreen mode Exit fullscreen mode
python -m pytest test_pricing_table.py -q
Enter fullscreen mode Exit fullscreen mode

The second test documents order as policy. Do not sort events in the function.

Sorting those events would hide rule sequence. Local pytest is the only merge gate here.

Illustrative predicate matrix

Do not copy money totals from prose. Generate cents with the recorder. The matrix below only shows expected booleans.

id bulk vip member holiday coupon_ok surge
guest_small_us false false false false false false
member_bulk_us true false true false false false
vip_holiday_eu false true true true false false
member_coupon false false true false true false
guest_coupon_ignored false false false false false false
us_surge_july4 false false false true false true
coupon_floor false false true false true false
vip_bulk_holiday_surge true true true true true true

Guest coupons stay inert on purpose. VIP still sets member true. That boolean split is part of the contract.

Step 5 — Extract one predicate only

The only allowed change is the holiday predicate. Introduce one helper named is_holiday for region and date.

Keep every other boolean inline in apply_price. Keep every if body on the same lines.

# allowed extract — example only
def is_holiday(region: str, today: date) -> bool:
    marks = HOLIDAYS.get(region, set())
    return (today.month, today.day) in marks
Enter fullscreen mode Exit fullscreen mode

Replace only the holiday local with the helper.

holiday = is_holiday(order.region, today)
Enter fullscreen mode Exit fullscreen mode

Re-run the same two tests after the extract. If cents drift, revert the extract immediately.

If events drift, revert that extract as well. If predicates drift, revert the extract without debate.

Do not extract member in the same diff. Do not merge vip and member in this diff.

Do not move multipliers into a dict. Those are later changes with new rows.

Step 6 — Use a model only after the table is green

A coding model can propose the holiday extract. It should not rewrite apply_price before the pin.

Paste the function, the golden table, and one instruction. The instruction must extract is_holiday and nothing else.

Disclosure: This article was prepared as part of MonkeyCode's product outreach. MonkeyCode offers free model access and a free server option.

That pair is useful as a disposable scratch space. You still run pytest on your machine.

The golden JSON file remains the only contract. Reject any patch that edits bulk, coupon, or surge.

Keep the model prompt short and mechanical. Name the new function in that prompt.

Name the files that must not change. Discard the session if the diff exceeds the holiday predicate.

Flags belong in the fixture, not in os.environ

Reading os.environ inside apply_price splits the table. The surge flag must be an argument.

Import-time env reads are a different pin. Do not mix those pins in one extract.

How to read a failing cell

A cents mismatch without an events mismatch is arithmetic. An events mismatch without a cents mismatch is a silent extra rule.

A predicates mismatch with both outputs stable is a renamed boolean. Treat that as a contract break anyway.

What the table will not catch

It will not catch races between flags and dates. It will not catch float remnants in other languages. It will not catch logger configuration changes at all.

It will not catch region codes absent from fixtures. It will not catch leap-day holidays you never listed.

Integer cents avoid one class of error. They still do not freeze tax rules.

They do not freeze currency rounding laws. Add those rules as later dedicated tables.

Who should not use this approach

Skip this if the function is a certified payments module. Skip this if reviewers cannot read JSON diffs.

Skip this if you need concurrent mutation of flags. Skip this if the next edit is a full rewrite.

Skip this if no owner can explain each event token. A model session is not a substitute owner.

If nobody can say why coupon requires member, stop. Ownership of each token comes before any extract.

Closing rule

Pin predicates, events, cents, and event order. Extract exactly one named predicate after that pin.

Re-run the table before you merge. That short sequence is the whole method. Keep the golden table in review, not the model transcript.

Top comments (0)