Clean = Readable + Understanding
-
is
-
can
-
has/icludes/contains
-
should/needs
Create | Create an instance, used in instantiated and factory methods |
Initialize | Initializes the properties and settings for the instance |
Uninitialize | Clean up the properties and Settings of the instance, as opposed to Initialize |
Load | Load content from the configuration |
Destroy | Destroy instance |
Get | Get properties, things you can get directly |
Fetch | Data from network |
Calculate | Data need to be calculated |
Read | Data from files or configurations |
Query | Data from DB |
Find | Data from DB or list container |
Receive | Receive files or messages |
Pull | Pull |
Set | Set properties |
Write | Write into files or configuration |
Put | Put in |
Push | Save in, push messages |
Reset | Reset sign or status |
Refresh | Refresh page or cache |
Update | Update configuration or status |
Add | Add things |
Append | Add to the tail |
Insert | Insert someplace |
Delete | Delete ≈ Remove |
Remove | Remove ≈ Delete |
Open | Open file |
Start | Start workflow |
Launch | Launch program or server |
Close | Close file |
Stop | Stop workflow |
Pause | Pause workflow which would start again |
Finish | Workflow done |
Filter | Filter data with conditions |
Merge | Merge data with conditions |
Concat | Add to the tail |
Split | Split |
Deduplicate | Remove duplicate |
Reverse | Reverse |
Sort | Sort |
Fill | Overwrite data |
Parse | Parse to a specific format, parse and extract content |
Analyse | Data that can not easily get |
Convert | Convert to another type |
Format | Format data |
Validate | Data Verification |
Ensure | Validate the expected value |
Compose | Compose multiple parts to a result |
Encode | Encode |
Decode | Decode |
Encrypt | Encrypt |
Decrypt | Decrypt |
Backup | Backup |
Restore | Restore |
Import | Import from a special format file |
Export | Export to a special format file |
Compress | Compress |
Decompress | Decompress |
Clean functions should be small and do only one thing
Keep the number of parameters small. Use a dictionary or Object to group multiple parameters
Do not mix levels of abstraction in a function
// Need to understand and interpret the different steps
if(!email.includes('@')) {
console.log('Invalid email');
} else {
const user = new User(email);
user.save();
}
// Just need to read the different steps
if(!isEmail(email)) {
showError('Invalid email');
} else {
saveNewUser(email);
}
-
Same input always returns same result
-
No side effects during execution(console, change the external data, API calling)
// Not a pure function
function connectDB() {
const didConnect = database.connnect();
if(didConnect) {
return true;
} else {
console.log('Could not connect to database!');
return false;
}
}
// Optimize to a pure function
function initApp() {
const success = connectDB();
if(!success) {
console.log('Could not connect to database!');
}
// ...
}
function connectDB() {
const didConnect = database.connnect();
return didConnect;
}
// Hard to test the method partially
function addProduct(name, price) {
if(!name || name.trim() === '' || !price || price < 0 ) {
console.log('Invalid input - product was not created.');
return;
}
const product = {
id: name + '-' + Math.random().toString(),
name: name,
price: price
};
database.insert('products', product);
return product;
}
// Split each process
function addProduct(name, price) {
validateProductData(name, price);
const savedProduct = saveProduct(name, price);
return savedProduct;
}
function validateProductData(name, price) {
if(!inputIsValid(name, price)) {
throw new Error('Invalid input - product was not created.');
}
}
function inputIsValid(name, price) {
return !isEmpty(name) && hasMinValue(price, 0);
}
// Call methods, hard to understand
printWelcomeWords(true);
calFinalAmount(200, true);
function printWelcomeWords(isActivated) {
if (isActivated) {
console.log('Welcome! Please select your product.');
} else {
console.log('Welcome! Please activate your account.');
}
}
function calFinalAmount(originalAmount, isChild) {
if (isChild) {
return originalAmount * 0.5;
} else {
return originalAmount;
}
}
// Call methods, easy to understand
printWelcomeWordsOnActivated();
calFinalAmountForChild(200);
calFinalAmountForAdult(200);
function printWelcomeWordsOnActivated() {
console.log('Welcome! Please select your product.');
}
function printWelcomeWordsOnUnactivated() {
console.log('Welcome! Please activate your account.');
}
function calFinalAmountForChild(originalAmount) {
return originalAmount * 0.5;
}
function calFinalAmountForAdult(originalAmount) {
return originalAmount;
}
- Method within a class only interact with its direct friends
- The variables, method parameters and method return objects within the class
- The local variables are not direct friends.
// Violates the Law of Demeter, interact with local variable: Employee
public class Company {
private Manager manager = new Manager();
public void printEmployees(String name) {
List<Employee> employeeList = manager.getEmployees(name);
for(Employee employee : employeeList) {
System.out.println(employee.getName());
}
}
}
// Follows the Law of Demeter.
public class Company {
private Manager manager = new Manager();
public void printEmployees(String name) {
manager.printEmployee(name);
}
}
- 'S' and 'O' make code clean
- Single responsibility Principle
- each class does one thing, as small as possible
- Open-closed Principle
- a class should be open for extension but closed for modification
- Reduce redundant code
- Single responsibility Principle
- High cohesion
- how well the methods of a class are using the class properties
let mapCalculate = {
'add': function (num1, num2) {
return num1 + num2;
},
'sub': function (num1, num2) {
return num1 - num2;
},
'mul': function (num1, num2) {
return num1 * num2;
},
'div': function (num1, num2) {
return num1 / num2;
},
}
function calculate(command, num1, num2) {
return mapCalculate[command](num1, num2);
}
class Calculator {
constructor() {
this.strategy = null;
}
// Strategy can be changed dynamically
setStrategy(strategy) {
this.strategy = strategy;
}
calculateResult(num1, num2) {
return this.strategy.execute(num1, num2);
}
}
class Add {
execute(num1, num2) {
return num1 + num2;
}
}
class Sub {
execute(num1, num2) {
return num1 - num2;
}
}
//...
Keep lines short
- Good code does not need too many comments
- Only use when:
- Explanations that can’t be replaced by good naming
- Warnings
- Todo notes
// C++: snprintf, stringstream
// C#: string.Format
// Java: String.format
let userId = 'foo';
let userName = 'bar';
let userInfo = 'User Info: Id: ${userId} Name: ${userName}.';
let headerContent = 'start | ${userId} | ${userName} | end';