Each routine does one thing and does it well - extract when routines have multiple responsibilities
A routine should do ONE thing and do it well. This is called functional cohesion - the strongest, best kind of cohesion.
Core principle: If a routine's description contains "and", it's doing too many things. Extract into focused routines.
Goal: Improve intellectual manageability. The more focused a routine, the easier to understand, test, modify, and reuse.
Proactively (writing new code):
Reactively (improving existing code):
Warning signs routine needs focus:
One thing means one level of abstraction:
ā Does one thing:
def calculate_total_price(items, tax_rate):
"""Calculate total price of items including tax."""
subtotal = sum(item.price * item.quantity for item in items)
tax = subtotal * tax_rate
return subtotal + tax
Single purpose: price calculation. All statements at same abstraction level (arithmetic).
ā Does multiple things:
def handle_order(order_data, user_id):
"""
Process order:
- Validate order data
- Calculate total with tax
- Apply discount codes
- Check inventory
- Create order record
- Send confirmation email
- Update user history
- Return confirmation
"""
validated = validate_order_data(order_data, user_id) # Thing 1
subtotal = calculate_subtotal(validated["items"]) # Thing 2
discount = apply_discount(subtotal, validated.get("discount_code")) # Thing 3
tax = calculate_tax(subtotal - discount, validated["tax_rate"]) # Thing 4
total = subtotal - discount + tax # Thing 5
check_inventory(validated["items"]) # Thing 6
order_record = create_order_record(...) # Thing 7
send_confirmation_email(...) # Thing 8
update_user_history(user_id, order_record["order_id"]) # Thing 9
return {...} # Thing 10
Description has 8 "and"s. Does 10 different things. Violates single responsibility.
Group related statements, extract into focused routine:
Before (orchestrator does everything):
def handle_order(order_data, user_id):
# Validation (lines 1-10)
validated = validate_order_data(order_data, user_id)
# Pricing (lines 11-15)
subtotal = calculate_subtotal(validated["items"])
discount = apply_discount(subtotal, validated.get("discount_code"))
tax = calculate_tax(subtotal - discount, validated["tax_rate"])
total = subtotal - discount + tax
# Inventory (lines 16-20)
check_inventory(validated["items"])
# Persistence (lines 21-30)
order_record = create_order_record(...)
# Notifications (lines 31-35)
send_confirmation_email(...)
update_user_history(user_id, order_record["order_id"])
return {...}
After (each phase is focused routine):
def handle_order(order_data, user_id):
"""Single responsibility: Orchestrate order processing."""
validated_order = validate_order_request(order_data, user_id)
pricing = calculate_order_pricing(validated_order)
verify_inventory_available(validated_order)
order = create_and_save_order(validated_order, pricing, user_id)
send_order_notifications(order)
return create_confirmation_response(order, pricing)
def validate_order_request(order_data, user_id):
"""Single responsibility: Validate order data."""
# Just validation
def calculate_order_pricing(validated_order):
"""Single responsibility: Calculate prices."""
# Just pricing math
def verify_inventory_available(validated_order):
"""Single responsibility: Check inventory."""
# Just inventory check
def create_and_save_order(validated_order, pricing, user_id):
"""Single responsibility: Persist order."""
# Just database operations
def send_order_notifications(order):
"""Single responsibility: Send notifications."""
# Just email/notifications
Each routine now has single, clear purpose.
If routine mixes high and low-level operations, extract low-level:
ā Mixed abstraction levels:
def process_report():
# High level
data = fetch_data()
# LOW level detail
for i in range(len(data)):
if data[i] is not None and data[i] > 0:
normalized = (data[i] - min_val) / (max_val - min_val)
data[i] = normalized
# High level
generate_output(data)
ā Consistent abstraction:
def process_report():
# All high level
data = fetch_data()
normalized_data = normalize_values(data) # Low-level extracted
generate_output(normalized_data)
def normalize_values(data):
# Low-level details isolated here
result = []
for value in data:
if value is not None and value > 0:
normalized = (value - min_val) / (max_val - min_val)
result.append(normalized)
return result
If conditional logic is complex, extract to well-named boolean function:
ā Complex inline condition:
if (user.age >= 18 and user.has_account and
user.account_balance > minimum and not user.is_suspended and
user.verified_email):
allow_purchase()
ā Extracted condition:
if is_eligible_for_purchase(user):
allow_purchase()
def is_eligible_for_purchase(user):
"""Single responsibility: Determine purchase eligibility."""
return (user.age >= 18 and
user.has_account and
user.account_balance > minimum and
not user.is_suspended and
user.verified_email)
Benefits: Name explains WHAT checking, function explains HOW.
| Sign Routine Needs Focus | Extraction Technique |
|---|---|
| Description has "and" | Extract each responsibility |
| Routine > 200 lines | Extract logical sections |
| Parameters > 7 | Group related params, extract |
| Hard to name | Clarify purpose, split if multiple |
| Hard to test | Extract testable pieces |
| Mixed abstraction levels | Extract low-level details |
| Deep nesting | Extract nested logic |
| Long parameter list | Extract to class or group params |
| Does A, B, C, D | Extract B, C, D into focused routines |
The gold standard: Routine does one thing and only that thing.
Always aim for functional cohesion.
ā Coincidental (worst):
def miscellaneous_functions():
initialize_printer()
calculate_payroll()
sort_personnel_records()
# Unrelated things in one routine - terrible
ā ļø Temporal (weak):
def startup():
initialize_database()
initialize_ui()
initialize_logging()
# Related by WHEN (startup), not by WHAT
ā Functional (best):
def calculate_employee_pay(employee, hours_worked):
# Does ONE thing: calculate pay
hourly_rate = employee.rate
gross_pay = hourly_rate * hours_worked
deductions = calculate_deductions(gross_pay)
return gross_pay - deductions
Routine name reveals focus:
| Name | Focus Assessment |
|---|---|
calculateTotal() |
ā Focused - one clear purpose |
processData() |
ā Vague - what processing? |
getUserAndValidate() |
ā Two things ("and" in name) |
initializeSystemData() |
ā ļø Might be multiple things |
handleUserRegistrationAndWelcomeEmail() |
ā Obviously two things |
If you struggle to name a routine clearly, it probably does too many things.
Extract until naming is easy.
Research shows: Routines with > 7 parameters correlate with higher error rates.
Why? Many parameters suggest routine is doing too much.
ā Too many parameters:
def create_order(user_id, items, shipping_addr, billing_addr,
discount_code, tax_rate, payment_method, notes):
# 8 parameters - probably doing too much
ā Group related parameters:
def create_order(user_id, order_details):
# 2 parameters - order_details is an object containing the data
ā Or extract responsibilities:
def create_order(validated_order, calculated_pricing):
# Validation and pricing are separate responsibilities
# This routine just creates the order record
No hard limit, but:
Exception: Generated code, complex state machines, extensive error handling
Question to ask: Can I extract logical sections without making code worse?
Default: When in doubt, extract. Extraction rarely makes code worse.
# ā
Extract complex logic
def calculate_mortgage_payment(principal, annual_rate, years):
monthly_rate = convert_to_monthly_rate(annual_rate)
num_payments = years * 12
return calculate_monthly_payment(principal, monthly_rate, num_payments)
def convert_to_monthly_rate(annual_rate):
return annual_rate / 12 / 100
def calculate_monthly_payment(principal, monthly_rate, num_payments):
return principal * (monthly_rate * (1 + monthly_rate)**num_payments) / \
((1 + monthly_rate)**num_payments - 1)
# ā
Extract validation to focused routine
def process_payment(payment_data):
validate_payment_data(payment_data) # Extracted
return execute_payment_transaction(payment_data)
def validate_payment_data(data):
"""Single responsibility: validation."""
# All validation logic here
# ā
High-level routine calls lower-level focused routines
def generate_report():
# High-level orchestration
data = collect_report_data()
analysis = analyze_data(data)
formatted_report = format_report_output(analysis)
save_report(formatted_report)
return formatted_report
ā God function (does everything):
def handle_user_request():
# Validation
# Authentication
# Authorization
# Business logic
# Database operations
# Logging
# Email sending
# Response formatting
# 300 lines doing 8 different things
ā Focused orchestrator:
def handle_user_request(request):
"""Single responsibility: Orchestrate request handling."""
user = authenticate_user(request)
authorize_action(user, request.action)
result = execute_business_logic(request, user)
save_to_database(result)
send_notifications(user, result)
return format_response(result)
ā Mixed abstraction levels:
def process_order(order):
validate_order(order) # High level
# Low level details mixed in
for item in order.items:
db.execute("UPDATE inventory SET quantity = quantity - ? WHERE id = ?",
(item.quantity, item.product_id))
send_confirmation(order) # High level
ā Consistent abstraction:
def process_order(order):
"""High-level orchestration."""
validate_order(order)
update_inventory(order.items) # Low-level extracted
send_confirmation(order)
def update_inventory(items):
"""Low-level: Update database."""
for item in items:
db.execute("UPDATE inventory SET quantity = quantity - ? WHERE id = ?",
(item.quantity, item.product_id))
ā "Utility" routine (does miscellaneous things):
def utils():
# Initializes printer
# Calculates payroll
# Sorts records
# Unrelated things - coincidental cohesion (worst)
ā Focused routines:
def initialize_printer(): ...
def calculate_payroll(): ...
def sort_personnel_records(): ...
Listen to your description of the routine:
ā "This routine validates the input AND calculates the result AND sends email" ā Three responsibilities. Extract 2 of them.
ā "This gets user data AND formats it for display" ā Two responsibilities (retrieval and formatting). Extract formatting.
ā "This calculates the shipping cost based on weight and destination" ā One responsibility (calculation). The "and" lists parameters, not responsibilities.
If description has "and" connecting actions ā routine does too many things.
Need to write/review a routine
ā
Does it do ONE thing?
āā YES ā Good, keep it focused
āā NO ā Does description have "and"?
āā YES ā Extract responsibilities
āā Mixed abstraction levels?
āā YES ā Extract low-level details
āā > 200 lines?
āā YES ā Find natural boundaries, extract
āā > 7 parameters?
āā YES ā Group params or extract
āā Hard to name?
āā YES ā Clarify purpose, maybe split
āā Keep as-is, it's focused
Original (unfocused):
def handle_order(order_data, user_id):
# Validate (responsibility 1)
if not order_data.get("items"):
raise ValueError("No items")
if not user_id:
raise ValueError("No user")
# Calculate pricing (responsibility 2)
subtotal = sum(item["price"] * item["qty"] for item in order_data["items"])
tax = subtotal * 0.08
total = subtotal + tax
# Check inventory (responsibility 3)
for item in order_data["items"]:
if inventory[item["id"]] < item["qty"]:
raise InventoryError()
# Create record (responsibility 4)
order_id = f"ORD-{datetime.now().isoformat()}"
order = {"id": order_id, "user_id": user_id, "total": total}
save_order(order)
# Send email (responsibility 5)
send_email(user_id, f"Order {order_id} confirmed")
return order
Extracted (focused routines):
def handle_order(order_data, user_id):
"""Single responsibility: Orchestrate order flow."""
validate_order_request(order_data, user_id)
pricing = calculate_pricing(order_data)
verify_inventory(order_data)
order = create_order(order_data, pricing, user_id)
notify_user(order, user_id)
return order
def validate_order_request(order_data, user_id):
"""Single responsibility: Validation."""
if not order_data.get("items"):
raise ValueError("No items")
if not user_id:
raise ValueError("No user")
def calculate_pricing(order_data):
"""Single responsibility: Pricing."""
subtotal = sum(item["price"] * item["qty"] for item in order_data["items"])
tax = subtotal * 0.08
return {"subtotal": subtotal, "tax": tax, "total": subtotal + tax}
def verify_inventory(order_data):
"""Single responsibility: Inventory check."""
for item in order_data["items"]:
if inventory[item["id"]] < item["qty"]:
raise InventoryError(f"Insufficient inventory for {item['id']}")
def create_order(order_data, pricing, user_id):
"""Single responsibility: Order creation."""
order_id = f"ORD-{datetime.now().isoformat()}"
order = {"id": order_id, "user_id": user_id, "total": pricing["total"]}
save_order(order)
return order
def notify_user(order, user_id):
"""Single responsibility: Notification."""
send_email(user_id, f"Order {order['id']} confirmed")
Benefits:
Small, focused routines are easier to comprehend:
# ā
Easy to test focused routine
def calculate_tax(amount, rate):
return amount * rate
# Test with various inputs, done
# ā Hard to test unfocused routine
def handle_order(...):
# Does 8 things - need to test all 8 in combination
# Need mocks for email, database, inventory, etc.
Focused routines have clear boundaries:
# ā
Can reuse focused routines
def handle_order(...):
validate_order_request(...) # Reusable
def handle_return(...):
validate_order_request(...) # Same validation, different context
Breaking into focused pieces makes each piece simpler:
Mental load: Understand 50 lines vs understand 200 lines.
Some routines are naturally longer:
Question: Are there natural extraction points that would improve clarity?
If no ā keep as-is. If yes ā extract.
For each routine, check:
If any "no" ā consider extracting.
From Code Complete:
From baseline testing:
With this skill: Both helpers AND orchestrators stay focused.
For extracting during refactoring: See skills/designing-before-coding - sometimes one pseudocode line explodes to many code lines, indicating need to extract
For complexity reduction: See skills/architecture/reducing-complexity - focused routines reduce mental juggling required