Refactor a Legacy Function Safely in Cursor

Point Cursor at a messy function and get a behaviour-preserving refactor: characterization tests first, then the rewrite, then a diff explanation, so nothing changes except readability.

by Nobodylovesyou ▲ 0 📋 0 copies 👁 1 ⭐ Featured

Fill in the variables

Prompt


            

Example input

Language: Python 3.12
Framework: none (pytest for tests)
Purpose: Compute the discounted total of a cart. Items are dicts with price and qty. Coupon codes: "SAVE10" gives 10% off, "FREESHIP" sets shipping to 0; shipping is 5.99 unless subtotal >= 50.
Problems: nested ifs, magic numbers, a variable called "x", I don't trust the rounding.
Constraints: keep the signature total(items, coupon=None) -> float, no new dependencies
Code:
def total(items, coupon=None):
    x = 0
    for i in items:
        if i["qty"] > 0:
            x = x + i["price"] * i["qty"]
    if coupon:
        if coupon == "SAVE10":
            x = x - x * 0.1
        else:
            if coupon == "FREESHIP":
                return round(x, 2)
    if x >= 50:
        return round(x, 2)
    else:
        return round(x + 5.99, 2)

Example output generated with Claude Sonnet 4.6 from the example input above

Behaviour - Sums price × qty for items with qty > 0; items with qty 0 or negative are ignored. - "SAVE10" reduces the subtotal by 10% before the shipping check, so the discount can push a cart below the $50 free-shipping threshold and add shipping. - "FREESHIP" returns the subtotal with no shipping and exits early, so free shipping applies at any subtotal. - Any other coupon string is silently ignored (treated as no coupon). - Shipping is 5.99 when the (possibly discounted) subtotal is under 50; free at 50 or more. Rounding to 2 decimals happens once at the end, on the final float. Suspected bugs (kept as is, see the end): unknown coupons do not raise; floating-point rounding on money; the discount-before-threshold ordering may be unintended. Tests ```python import pytest from cart import total def test_empty_cart_pays_shipping(): assert total([]) == 5.99 def test_subtotal_under_threshold_adds_shipping(): assert total([{"price": 10.0, "qty": 2}]) == 25.99 def test_subtotal_at_threshold_is_free_shipping(): assert total([{"price": 25.0, "qty": 2}]) == 50.0 def test_zero_and_negative_qty_ignored(): assert total([{"price": 10.0, "qty": 0}, {"price": 10.0, "qty": -1}]) == 5.99 def test_save10_applies_before_shipping_threshold(): # 52 * 0.9 = 46.8 -> under 50, so shipping is added (current behaviour) assert total([{"price": 52.0, "qty": 1}], "SAVE10") == 52.79 def test_freeship_skips_shipping_at_any_subtotal(): assert total([{"price": 10.0, "qty": 1}], "FREESHIP") == 10.0 def test_unknown_coupon_is_ignored(): assert total([{"price": 10.0, "qty": 1}], "NOPE") == 15.99 def test_rounding_to_cents(): assert total([{"price": 0.1, "qty": 3}]) == 6.29 ``` Refactored code ```python SAVE10_RATE = 0.10 SHIPPING_FEE = 5.99 FREE_SHIPPING_THRESHOLD = 50.0 def total(items, coupon=None): subtotal = _subtotal(items) if coupon == "SAVE10": subtotal = _apply_percentage_discount(subtotal, SAVE10_RATE) if coupon == "FREESHIP": return round(subtotal, 2) return round(subtotal + _shipping_for(subtotal), 2) def _subtotal(items): return sum(item["price"] * item["qty"] for item in items if item["qty"] > 0) def _apply_percentage_discount(amount, rate): return amount - amount * rate def _shipping_for(subtotal): return 0.0 if subtotal >= FREE_SHIPPING_THRESHOLD else SHIPPING_FEE ``` Changes - Extracted the loop into `_subtotal`, keeping the `qty > 0` filter exactly. - Replaced `x` with `subtotal`; named the three magic numbers as module constants. - Flattened the nested coupon `if`s into two independent checks; order preserved (discount, then the FREESHIP early return, then shipping), so the SAVE10-before-threshold behaviour is unchanged and `test_save10_applies_before_shipping_threshold` still passes. - Moved the shipping rule into `_shipping_for`; the `>= 50` boundary is unchanged (`test_subtotal_at_threshold_is_free_shipping`). - Rounding still happens once, on the final value, so `test_rounding_to_cents` and `test_freeship_skips_shipping_at_any_subtotal` pass. - Signature `total(items, coupon=None)` and the float return are unchanged. All eight tests pass under both versions. Suspected bugs (follow-up, not applied) 1. Unknown coupon codes are silently ignored. Recommended: raise `ValueError(f"unknown coupon: {coupon}")` or return a validation result, and add a test. 2. Money is computed in binary floats; `0.1 * 3` style errors are hidden by the final rounding but can surface in larger carts. Recommended: use `decimal.Decimal` with `ROUND_HALF_UP` and store cents as integers at the boundary. 3. SAVE10 is applied before the free-shipping check, so a 10% discount can cost a customer $5.99 in shipping. If unintended, apply the discount after determining shipping; this changes outputs, so decide with the product owner first.

Why this works & how to customize

Why it works: "refactor this" without guard rails is how AI tools introduce subtle behaviour changes. Characterization tests first turn the refactor into a verifiable operation, and the "keep the bug, flag it" rule stops the model from quietly changing semantics while cleaning up. Restating behaviour before touching code also surfaces misunderstandings early.

How to customize: in Cursor, select the function, open the chat with the selection attached and paste this prompt with the code field left as "the selected code". In Claude Code, run it on a file path and ask it to write the tests to the test directory before editing. For larger modules, run the Behaviour step alone first and confirm it before allowing the rewrite. If you do want the bugs fixed, move them from "problems" to an explicit instruction: "fix bug 3 and update the tests accordingly".
Open in Cursor → 🍴 Fork

Comments (0)

Sign in to comment.

No comments yet.