Functions
16 min
A function that does ONE clear thing is easier to name, test, reuse, and reason about than one that does five things at once. The single responsibility principle is the guiding rule: if you find yourself struggling to name a function without using "and," it's probably doing too much.
def calculate_total_with_discount(price, quantity, discount_pct):
subtotal = price * quantity
discount = subtotal * (discount_pct / 100)
return round(subtotal - discount, 2)
print(calculate_total_with_discount(10, 3, 10)) # 27.0
One job — computing a discounted total — clearly named, easy to test in isolation.
The 'bad' version works fine for a single use case -- the split version is what actually stays maintainable once you need to validate orders somewhere else, or format the total differently in a different context.
Function LENGTH is a rough proxy (not a perfect one, but a genuinely useful early-warning sign) for whether a function has taken on too many responsibilities:
def is_function_too_long(line_count, max_lines=20):
return line_count > max_lines
There's no universally "correct" line limit — some teams enforce 20, some 50, some none at all — but if a function has grown so long you need to scroll to see its whole body, that's usually a sign it's doing several distinct things that could be split into smaller, separately named, separately testable pieces.
Write `calculate_total_with_discount(price, quantity, discount_pct)`: one clearly-named function, one job — compute the subtotal, apply the percentage discount, and return the result rounded to 2 decimals.
Write `is_function_too_long(line_count, max_lines=20)`, returning True if `line_count` exceeds `max_lines` — a simple heuristic for 'this function is probably doing too much.'
What's the 'single responsibility principle' as applied to functions?
You can write a single-responsibility function with a clear name, and recognize function length as a rough signal for when a function is doing too much.