These transformations are safe because they can be verified without tests.
When: Code is too long, has comments explaining sections, or you need to reuse a portion.
Why safe: Extracted code is literally copy-pasted. Compiler catches missing variables.
# Before
def process_order(order):
# validate
if order.total < 0:
raise ValueError("Invalid total")
if not order.items:
raise ValueError("No items")
# calculate discount
discount = 0
if order.total > 100:
discount = order.total * 0.1
return order.total - discount
# After
def process_order(order):
validate_order(order)
discount = calculate_discount(order.total)
return order.total - discount
def validate_order(order):
if order.total < 0:
raise ValueError("Invalid total")
if not order.items:
raise ValueError("No items")
def calculate_discount(total):
if total > 100:
return total * 0.1
return 0When: Function body is as clear as its name, or you need full picture before re-extracting differently.
Why safe: Mechanical replacement, compiler catches type mismatches.
// Before
func isAdult(age int) bool {
return age >= 18
}
func canVote(person Person) bool {
return isAdult(person.Age) && person.Registered
}
// After (if isAdult adds no clarity)
func canVote(person Person) bool {
return person.Age >= 18 && person.Registered
}When: Name doesn't reflect purpose, or purpose has changed.
Why safe: Compiler/linter catches all references. Find-and-replace with whole-word matching.
Caution: Watch for string references (API endpoints, serialization), dynamic access, cross-file public APIs.
When: Function more closely related to another module/class.
Why safe: Import errors catch missing references.
// Before: in utils.dart
String formatCurrency(double amount) {
return '\$${amount.toStringAsFixed(2)}';
}
// After: moved to money.dart (where Money class lives)
// Update all imports from 'utils.dart' to 'money.dart'When: Expression is complex or used multiple times.
Why safe: Pure mechanical extraction, no logic change.
# Before
if user.subscription.plan.price > 100 and user.subscription.plan.price < 500:
apply_mid_tier_discount(user.subscription.plan.price)
# After
price = user.subscription.plan.price
if price > 100 and price < 500:
apply_mid_tier_discount(price)When: Variable adds no explanatory value.
Why safe: Direct substitution.
// Before
const basePrice = order.basePrice;
return basePrice;
// After
return order.basePrice;When: Loop does multiple unrelated things.
Why safe: Same iterations, same operations, just separated.
// Before
var sum int
var product int = 1
for _, v := range values {
sum += v
product *= v
}
// After
var sum int
for _, v := range values {
sum += v
}
var product int = 1
for _, v := range values {
product *= v
}Note: Yes, "slower" (two loops). Optimize later if profiling shows it matters. Clarity enables further refactoring.
When: Deep nesting obscures main logic.
Why safe: Same conditions, same outcomes, different structure.
# Before
def get_payment_amount(employee):
if employee.is_separated:
result = separated_amount(employee)
else:
if employee.is_retired:
result = retired_amount(employee)
else:
result = normal_amount(employee)
return result
# After
def get_payment_amount(employee):
if employee.is_separated:
return separated_amount(employee)
if employee.is_retired:
return retired_amount(employee)
return normal_amount(employee)A "seam" is where you can alter program behavior without modifying code at that location.
Pass a dependency rather than hard-coding it.
# Before: hard to test or modify behavior
def send_notification(user, message):
client = SMTPClient("mail.server.com")
client.send(user.email, message)
# After: seam at the client parameter
def send_notification(user, message, client=None):
if client is None:
client = SMTPClient("mail.server.com")
client.send(user.email, message)Behavior controlled by configuration rather than code.
func ProcessPayment(amount float64) error {
if config.PaymentProvider == "stripe" {
return processStripe(amount)
}
return processSquare(amount)
}To extract common code from two places:
- Make them identical (even if that means temporary duplication)
- Then extract the common code
// Two similar but different functions
function processUserOrder(order: Order) {
validateUser(order.userId);
const tax = order.total * 0.08;
const shipping = 5.99;
return order.total + tax + shipping;
}
function processGuestOrder(order: Order) {
const tax = order.total * 0.08;
const shipping = order.items > 2 ? 0 : 5.99; // different!
return order.total + tax + shipping;
}
// Step 1: Make shipping calculation explicit in both
function processUserOrder(order: Order) {
validateUser(order.userId);
const tax = order.total * 0.08;
const shipping = calculateShipping(order, false);
return order.total + tax + shipping;
}
function processGuestOrder(order: Order) {
const tax = order.total * 0.08;
const shipping = calculateShipping(order, true);
return order.total + tax + shipping;
}
// Step 2: Now extract the common part
function calculateOrderTotal(order: Order, isGuest: boolean): number {
const tax = order.total * 0.08;
const shipping = calculateShipping(order, isGuest);
return order.total + tax + shipping;
}