The Agile Sprint & Business Rules: Stage 5 represents the pinnacle of the method. The AI adopts the persona of a Tech Lead handing over a “Jira Ticket” featuring a critical production bug. This simulates a real-world environment where developers must translate strict business logic (e.g., negative balances) into defensive code.
PROJ-1025: Refactor Refund Processor to Include Strict Validations
Hello team,
We’ve detected a critical issue in our payments module. Currently, the process_refunds function processes any transaction sent to it, which has resulted in negative balances and the issuance of invalid refunds (even negative ones that end up adding money to the user). Additionally, the script was mistakenly executed in the “testing” environment while pointing to the real database.
Your task is to refactor this script. I need you to use assert to guarantee the system environment’s integrity before running the process, and implement custom exceptions using raise to stop illogical transactions based on business rules, without crashing the whole program.
- The script must abort execution with an AssertionError if the API_MODE environment variable is not exactly “production”.
- For transactions with an amount less than or equal to zero, a ValueError must be raised and caught.
- For transactions where the refund amount exceeds the user’s current balance, a custom exception named InsufficientBalanceError must be raised and caught.
- Only valid transactions should update the balances, and rejected transactions must print the reason for failure.
For this project to run safely on your machine, you need to create a local configuration file. Create a .env file in your project root with the following content:
API_MODE=testing
import os
# Simulating reading our .env file for this development environment
os.environ["API_MODE"] = "testing"
def process_refunds(transactions, balances):
print(f"Current mode: {os.environ.get('API_MODE')}")
for tx in transactions:
user = tx["user"]
amount_float = float(tx["amount"])
# The balance is updated indiscriminately (BUG)
balances[user] = balances.get(user, 0.0) - amount_float
print(f"Refund of ${amount_float:,.2f} applied to {user}.")
return balances
db_balances = {"u_101": 500.0, "u_102": 150.0}
pending_refunds = [
{"user": "u_101", "amount": "50.0"},
{"user": "u_102", "amount": "200.0"},
{"user": "u_101", "amount": "-20.0"}
]
final_balances = process_refunds(pending_refunds, db_balances)
print("Final Balances:", final_balances)
Current mode: testing
Refund of $50.00 applied to u_101.
Refund of $200.00 applied to u_102.
Refund of $-20.00 applied to u_101.
Final Balances: {'u_101': 470.0, 'u_102': -50.0}
The Solution Boundary & Hints: To prevent passive reading, the prompt forces the student to implement the feature locally first. It provides strategic hints on logic (like how an assert works) but strictly hides the final code behind a visual barrier. This builds problem-solving resilience.
(Do not continue reading unless you have your own solution ready or are completely stuck)
import os
# We adjust the environment to production so the assert validates correctly
os.environ["API_MODE"] = "production"
class InsufficientBalanceError(Exception):
"""Exception for refunds that exceed the user's balance."""
pass
def process_secure_refunds(transactions, balances):
# Critical state validation with assert
assert os.environ.get("API_MODE") == "production", "The environment is not valid for processing financial operations."
print(f"Current mode: {os.environ.get('API_MODE')}")
for tx in transactions:
usr = tx["user"]
amount = float(tx["amount"])
current_balance = balances.get(usr, 0.0)
try:
# Business rule validations with raise
if amount <= 0:
raise ValueError(f"The amount {amount} is invalid (must be greater than 0).")
if amount > current_balance:
raise InsufficientBalanceError(f"The amount {amount} exceeds the available balance of {current_balance}.")
balances[usr] = current_balance - amount
print(f"Success: Refund of ${amount:,.2f} processed for {usr}.")
except (ValueError, InsufficientBalanceError) as err:
print(f"Rejected ({usr}): {err}")
return balances
db_balances = {"u_101": 500.0, "u_102": 150.0}
refunds = [
{"user": "u_101", "amount": "50.0"},
{"user": "u_102", "amount": "200.0"},
{"user": "u_101", "amount": "-20.0"}
]
final_balances = process_secure_refunds(refunds, db_balances)
print("\nFinal System Balances:")
# We sort the items to ensure consistent and identical output every time
for usr, balance in sorted(final_balances.items()):
print(f"{usr}: ${balance:,.2f}")
Architectural Review: The stage concludes with ‘Implementation Notes’. The AI doesn’t just provide the code; it explains why specific architectural decisions were made, elevating the student’s systems-thinking skills and technical vocabulary.
- Creating a Custom Exception: Defining class InsufficientBalanceError(Exception): pass allows us to create specific semantic errors for our business domain, making the code more readable and traceable.
- Use of assert: The assert line acts as a state “gatekeeper” in the development stage. It immediately guarantees that if a fundamental system configuration is missing or incorrect, the process will never iterate over sensitive data.
- Control Flow with Exceptions: We employ a try/except block directly inside the for loop. When a raise is triggered, the execution automatically jumps to the respective except, printing the rejection reason and thus protecting the balances dictionary from receiving corrupt alterations.
Current mode: production
Success: Refund of $50.00 processed for u_101.
Rejected (u_102): The amount 200.0 exceeds the available balance of 150.0.
Rejected (u_101): The amount -20.0 is invalid (must be greater than 0).
Final System Balances:
u_101: $450.00
u_102: $150.00
