Writing Maintainable Software
2025-11-06
Martin Fowler’s Definition
“Refactoring is a controlled technique for improving the design of an existing code base. Its essence is applying a series of small behavior-preserving transformations.”
Key Principles:
Surface indications that usually correspond to deeper problems in the system
Think: Warning signs, not bugs
Methods that do too much
Warning Signs
Class trying to do too much
Same code structure in multiple places
// In UserController.java
if (email == null || !email.contains("@")) {
throw new ValidationException("Invalid email");
}
// In RegistrationService.java
if (email == null || !email.contains("@")) {
throw new ValidationException("Invalid email");
}
// In ProfileUpdater.java
if (email == null || !email.contains("@")) {
throw new ValidationException("Invalid email");
}Warning
Changes must be made in multiple locations - error prone!
Methods with many parameters
Using primitives instead of small objects
Method more interested in another class
Tip
This method envies the Customer class - maybe it belongs there?
Code smells are local problems - but what about system-level issues?
Architectural Smells
Structural problems in how modules/components relate to each other
A matrix showing dependencies between system modules
| UI | DB | Auth | Utils | |
|---|---|---|---|---|
| UI | - | X | X | X |
| DB | - | X | ||
| Auth | X | X | - | X |
| Utils | X | X | X | - |
Warning
Problem: Everyone depends on everyone! (Cyclic dependencies)
. . .
| Utils | DB | Auth | UI | |
|---|---|---|---|---|
| Utils | - | |||
| DB | X | - | ||
| Auth | X | X | - | |
| UI | X | X | X | - |
. . .
Lower-level modules at top, high-level at bottom
When modules form dependency cycles
Problems
One module that everyone depends on
| Utils | Core | UI | DB | |
|---|---|---|---|---|
| Utils | - | |||
| Core | X | - | ||
| UI | X | X | - | |
| DB | X | X | - |
Core is a bottleneck
Similar to: God Class, but at architecture level
Stable modules depending on unstable ones
Stability Metrics (Robert Martin)
Instability = Efferent / (Afferent + Efferent)
The Smell
Stable module (low instability) → Depends on → Unstable module (high instability)
Example: Core business logic depending on experimental UI framework
Single change requires modifications across many modules
Adding a new field “customerType”:
Feature scattered across architecture
DSM Indicator: - Dense columns - No clear boundaries
Tip
Solution: Refactor to Feature-based or Layer-based modules
| A | B | C | D | |
|---|---|---|---|---|
| A | - | X | X | X |
| B | X | - | X | X |
| C | X | - | X | |
| D | X | - |
Cycles, high coupling
| D | C | B | A | |
|---|---|---|---|---|
| D | - | |||
| C | X | - | ||
| B | X | X | - | |
| A | X | X | X | - |
Clean hierarchy
How?
. . .
Key Insight from Baldwin
Modularity has economic value
. . .
. . .
Tip
Practice: Draw DSM for your current project - what do you see?
| Code Smells | Architectural Smells | |
|---|---|---|
| Scope | Methods, classes | Modules, components |
| Impact | Local readability | System-wide maintainability |
| Detection | Code review, IDE | DSM, metrics, tools |
| Fix Cost | Hours to days | Days to weeks |
| Fix Tools | Extract method, rename | Restructure modules, add layers |
Note
Both are important!
Golden Rule
Always have tests before refactoring!
When: Code fragment that can be grouped together
Benefits: Readability, reuse, testability
When: Name doesn’t reveal its purpose
Note
Code is read 10x more than it’s written
When: Complex expression is hard to understand
. . .
When: Literal numbers with unclear meaning
When: Parameters naturally go together
. . .
When: Conditional behavior based on object type
The Rule of Three (Beck’s Rule)
Task: Identify code smells and suggest refactorings
Time: 5 minutes individual work, then discussion
public class OrderProcessor {
public void processOrder(String custName, String custEmail,
String custPhone, String item1,
double price1, int qty1, String item2,
double price2, int qty2) {
// Calculate total
double t = price1 * qty1 + price2 * qty2;
// Apply discount
double d = 0;
if (t > 100) {
d = t * 0.1;
} else if (t > 50) {
d = t * 0.05;
}
t = t - d;
// Add tax
t = t * 1.13; // Print receipt
System.out.println("=============================");
System.out.println("Customer: " + custName);
System.out.println("Email: " + custEmail);
System.out.println("Phone: " + custPhone);
System.out.println("-----------------------------");
System.out.println(item1 + " x " + qty1 + " = $" +
(price1 * qty1));
System.out.println(item2 + " x " + qty2 + " = $" +
(price2 * qty2));
System.out.println("Discount: $" + d);
System.out.println("Tax: $" + (t - (t / 1.13)));
System.out.println("-----------------------------");
System.out.println("TOTAL: $" + t);
System.out.println("=============================");
// Send email
System.out.println("Sending email to " + custEmail);
}
}Hint: Look for at least 6 smells!
t, d single-letter variables {.fragment}. . .
public void processOrder(Order order) {
double subtotal = calculateSubtotal(order);
double discount = calculateDiscount(subtotal);
double total = calculateTotal(subtotal, discount);
Receipt receipt = new Receipt(order, subtotal, discount, total);
printReceipt(receipt);
sendConfirmationEmail(order.getCustomer(), receipt);
}
private double calculateSubtotal(Order order) {
return order.getItems().stream()
.mapToDouble(item -> item.getPrice() * item.getQuantity())
.sum();
}Essential Resources
Book: Refactoring: Improving the Design of Existing Code (2nd Edition) by Martin Fowler
Website: refactoring.com - Catalog of refactorings

Neil Ernst ©️ 2024-5