When programming, follow the camping rule: Always leave the code base healtheir than when you found it.
The true test of good code is how easy is it to change it
Wearing two hats when programming is useful:
- Programming new features
- Refactoring
- Improve the design of software
- Make existing code easier to understand
- Help finds bugs
- Long term faster programming
- Everyday incrementally - like watering plants
- Preparatory Refactoring - Making it easier to add a new feature
- Comprehension Refactoring - Making it easier to understand the codebase
- Litter-Pickup Refactoring - Understanding what the code is doing, but doing it badly.
You have to refactor when you run into ugly code - but excellent code needs plenty of refactoring too
The motivation behind Extract Function
is to move 'groupings' of logic into their own scope/responsibility.
The purpose is to:
- Allow code to be more modular and reusable
- Allows code to be more readable, when reviewing code, the reader can read the function name instead of deducing the details
- Can be kept as an inner closure or moved out to a greater scope
Before Refactor:
function printOwing(invoice) {
printBanner();
let outstanding = calculateOutstanding();
// Print details.
console.log(`name: ${invoice.customer}`);
console.log(`amount: ${outstanding}`);
}
After Refactor:
function printOwing(invoice) {
printBanner();
let outstanding = calculateOutstanding();
printDetails(outstanding);
function printDetails(outstanding) {
console.log(`name: ${invoice.customer}`);
console.log(`amount: ${outstanding}`);
}
}
Removes temporary variables and moves them to function calls, allows reduced duplication of using temp variables for logic or calculation also useful combied with Inline Variables.
Before Refactor:
pub fn calc_price(&self) -> f32 {
let base_price = self.quantity * self.price;
if base_price > 1000 as f32 {
return base_price * 0.95;
} else {
return base_price * 0.98;
}
}
After Refactor:
pub fn calc_price(&self) -> f32 {
self.base_price() * self.discount_factor()
}
fn base_price(&self) -> f32 {
self.quantity * self.price
}
fn discount_factor(&self) -> f32 {
let mut discount_factor = 0.98;
if self.base_price() > 1000 as f32 {
discount_factor -= 0.03
}
discount_factor
}
Code is easier to understand when related things are closer together.
Similar idea to organizing functions in order of when they are called.
Before:
const pricingPlan = retrievePricingPlan();
const order = retrieveOrder();
let charge;
const chargePerUnit = pricingPlan.unit;
After:
chargePerUnit should come after pricingPlan directly since chargePerUnit uses the object pricingPlan.
const pricingPlan = retrievePricingPlan();
const chargePerUnit = pricingPlan.unit();
const order = retriveOrder();
let charge;
Splits a loop that has multiple responsibilities into their own loop.
Many people use just one loop. But its actually easier to come back to the loop and read the code if the loop is split into two. This is because we only need to undestand one thing in each loop.
Before Refactor:
for (const number in something) {
a += number
b *= number
}
After Refactor:
From here we can consider an extract function
for (const number in something) {
a += number
}
for (const number in something) {
b *= number
}