Refactoring simplifying conditionals
Skill lifeodyssey/craftsmanship-skills/skills/refactoring-simplifying-conditionals
Agent Skills distilled from Clean Code & Refactoring. Install: npx skills add lifeodyssey/craftsmanship-skills
npx -y skills add lifeodyssey/craftsmanship-skills --skill refactoring-simplifying-conditionalsAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Techniques for simplifying conditional logic: Decompose Conditional, Consolidate Conditional Expression, Consolidate Duplicate Conditional Fragments, Remove Control Flag, Replace Nested Conditions with Guard Clauses, Replace Conditional with Polymorphism. All with Python before/after examples.
SKILL.md
10.8 KB, as published. Nobody here has run it
Simplifying Conditional Expressions
Conditional logic is often the hardest code to follow. These refactorings make conditionals clearer, flatter, and more expressive.
When to Use This Skill
Triggers:
- Nested if/else blocks are hard to follow
- Conditional expressions are long and complex
- The same code appears in multiple branches of a conditional
- A boolean flag controls flow through a loop or method
- A growing chain of if-elif-else or switch/match statements
- Conditional logic varies by type but the types don't use polymorphism
Don't Triggers:
- The problem is that a method is too long in general (use
refactoring-composing-methods) - The problem is about which class the logic belongs in (use
refactoring-moving-features) - You're trying to identify what's wrong at a high level (use
refactoring-code-smells)
Decompose Conditional
Problem: You have a complicated conditional (if-then-else) expression.
Solution: Extract the condition, the then-branch, and the else-branch into their own methods.
# BEFORE — complex conditional inlined
def calculate_charge(self, quantity, date):
if date.before(SUMMER_START) or date.after(SUMMER_END):
return quantity * self._winter_rate + self._winter_service_charge
else:
return quantity * self._summer_rate
# AFTER — each part named for what it means
def calculate_charge(self, quantity, date):
if self._is_winter(date):
return self._winter_charge(quantity)
else:
return self._summer_charge(quantity)
def _is_winter(self, date):
return date.before(SUMMER_START) or date.after(SUMMER_END)
def _winter_charge(self, quantity):
return quantity * self._winter_rate + self._winter_service_charge
def _summer_charge(self, quantity):
return quantity * self._summer_rate
Steps:
- Extract the condition into a method named for what it checks.
- Extract the then-branch into a method named for what it does.
- Extract the else-branch into a method named for what it does.
- Replace the conditional with calls to these methods.
Consolidate Conditional Expression
Problem: You have a sequence of conditional tests that all result in the same action.
Solution: Combine them into a single conditional with a logical operator.
# BEFORE — separate conditions, same result
def calculate_shipping(self, order):
if order.is_expired:
return 0
if order.is_digital:
return 0
if order.is_free_shipping:
return 0
return order.weight * SHIPPING_RATE
# AFTER — consolidated into one check
def calculate_shipping(self, order):
if order.is_expired or order.is_digital or order.is_free_shipping:
return 0
return order.weight * SHIPPING_RATE
Reverse case: If conditions are combined but shouldn't be, extract them into separate methods for clarity.
# BEFORE — combined but the reasons are different
def get_discount(self, customer):
if customer.is_vip or customer.referral_count > 5:
return 0.15
return 0.0
# AFTER — separate conditions for separate reasons
def get_discount(self, customer):
if self._has_vip_discount(customer):
return 0.15
if self._has_referral_discount(customer):
return 0.15
return 0.0
def _has_vip_discount(self, customer):
return customer.is_vip
def _has_referral_discount(self, customer):
return customer.referral_count > 5
Steps:
- Verify that the conditions don't have side effects.
- Combine the conditions using
andoror. - Test.
Consolidate Duplicate Conditional Fragments
Problem: The same code appears in all branches of a conditional expression.
Solution: Move the duplicated code outside the conditional.
# BEFORE — discount calculation duplicated in both branches
def calculate_total(self, customer, order):
if customer.is_vip:
discount = order.total * 0.1
order.total -= discount
self._send_receipt(order)
else:
discount = order.total * 0.05
order.total -= discount
self._send_receipt(order)
# AFTER — common code moved after the conditional
def calculate_total(self, customer, order):
if customer.is_vip:
discount = order.total * 0.1
else:
discount = order.total * 0.05
order.total -= discount
self._send_receipt(order)
Also works for code at the beginning of branches:
# BEFORE — common code at start of each branch
def process(self, data):
if data.is_valid:
sanitized = self._sanitize(data)
self._transform(sanitized)
else:
sanitized = self._sanitize(data)
self._log_error(sanitized)
# AFTER
def process(self, data):
sanitized = self._sanitize(data)
if data.is_valid:
self._transform(sanitized)
else:
self._log_error(sanitized)
Steps:
- Identify code that is identical in all branches.
- Move it before or after the conditional.
- Test.
Remove Control Flag
Problem: You have a variable that acts as a control flag for a series of boolean expressions.
Solution: Use break, return, or exception handling instead.
# BEFORE — control flag used to exit loop
def find_person(self, people):
found = False
result = None
for person in people:
if not found:
if person.name == "Don":
result = person
found = True
elif person.age > 25:
result = person
found = True
return result
# AFTER — return directly when found
def find_person(self, people):
for person in people:
if person.name == "Don":
return person
if person.age > 25:
return person
return None
Steps:
- Find the control flag variable.
- Replace assignments to the flag with
breakorreturn. - Remove the flag variable and its checks.
Replace Nested Conditions with Guard Clauses
Problem: A method has conditional behavior where the normal path is nested inside exception cases.
Solution: Use guard clauses to handle exceptional cases first and return early.
# BEFORE — deeply nested, hard to follow the main path
def calculate_pay(self, employee):
if employee.is_active:
if employee.is_contractor:
return employee.hourly_rate * employee.hours_workd * 0.9
else:
if employee.is_manager:
return employee.salary + employee.bonus
else:
return employee.salary
else:
return 0
# AFTER — guard clauses flatten the logic
def calculate_pay(self, employee):
if not employee.is_active:
return 0
if employee.is_contractor:
return employee.hourly_rate * employee.hours_worked * 0.9
if employee.is_manager:
return employee.salary + employee.bonus
return employee.salary
Steps:
- Identify the "special case" conditions — things that aren't the main flow.
- Replace each special case with a guard clause: check the condition and return early.
- After all guards, the remaining code is the normal flow.
When to use: When the main flow of a method is obscured by exceptional cases. If all branches are equally likely, nesting may be fine.
Replace Conditional with Polymorphism
Problem: You have a conditional that chooses different behavior depending on the type of an object.
Solution: Move each leg of the conditional into an overriding method in a subclass.
# BEFORE — switch on type
class Employee:
def __init__(self, emp_type, salary):
self.emp_type = emp_type
self.salary = salary
def calculate_bonus(self):
if self.emp_type == "engineer":
return self.salary * 0.1
elif self.emp_type == "manager":
return self.salary * 0.2
elif self.emp_type == "sales":
return self.salary * 0.15
else:
raise ValueError(f"Unknown type: {self.emp_type}")
def get_vacation_days(self):
if self.emp_type == "engineer":
return 15
elif self.emp_type == "manager":
return 20
elif self.emp_type == "sales":
return 12
else:
raise ValueError(f"Unknown type: {self.emp_type}")
# AFTER — polymorphism eliminates the conditionals
from abc import ABC, abstractmethod
class Employee(ABC):
def __init__(self, salary):
self.salary = salary
@abstractmethod
def calculate_bonus(self) -> float: ...
@abstractmethod
def get_vacation_days(self) -> int: ...
class Engineer(Employee):
def calculate_bonus(self) -> float:
return self.salary * 0.1
def get_vacation_days(self) -> int:
return 15
class Manager(Employee):
def calculate_bonus(self) -> float:
return self.salary * 0.2
def get_vacation_days(self) -> int:
return 20
class Sales(Employee):
def calculate_bonus(self) -> float:
return self.salary * 0.15
def get_vacation_days(self) -> int:
return 12
Steps:
- Create a base class with abstract methods for each conditional branch.
- Create a subclass for each type. Move each leg of the conditional into the appropriate subclass.
- Replace the conditional with polymorphic calls.
- Remove the type code field from the base class.
When to use: When the same type-based conditional appears in multiple places, and especially when adding a new type requires touching every conditional. If there's only one conditional and it's unlikely to grow, a simple if/else may be clearer.
Summary Table
| Refactoring | Problem | Solution |
|---|---|---|
| Decompose Conditional | Complex conditional expression | Extract condition, then, and else into named methods |
| Consolidate Conditional Expression | Multiple conditions with same result | Combine with and/or |
| Consolidate Duplicate Conditional Fragments | Same code in all branches | Move common code outside the conditional |
| Remove Control Flag | Boolean flag controlling flow | Use break/return instead |
| Replace Nested Conditions with Guard Clauses | Normal path buried in nesting | Handle exceptions first, return early |
| Replace Conditional with Polymorphism | Switch on type | Subclasses override methods |
Source: Martin Fowler, "Refactoring: Improving the Design of Existing Code" (2nd Edition)