Clean Code Practices
”Any fool can write code that a computer can understand. Good programmers write code that humans can understand.” — Martin Fowler.
Clean code is not about a rigid set of rules, but about a mindset. It is code that looks like it was written by someone who cares.
1. Meaningful Names
Names should reveal intent. Avoid generic names like data, info, or item.
Variable Names
- Use searchable names:
MAX_RETRY_ATTEMPTSis better than7. - Avoid encodings: Don’t use Hungarian notation (e.g.,
strName) or member prefixes (m_name). Modern IDEs handle types and scope. - Pronounceable: If you can’t discuss it in a meeting without spelling it out, it’s a bad name.
Method Names
- Verbs: Methods represent actions and should be verbs like
postPayment(),deletePage(), orsave(). - Accessors/Mutators: Prefix with
get,set, andis(for booleans).
2. Functions
Functions should be small and do one thing.
- Small: Aim for functions that fit on one screen without scrolling.
- Single Responsibility: A function should do one thing and do it well. If you can describe it using “and”, it probably does too much.
- Low Parameter Count: Ideally 0–2. Three requires a strong justification. More than three? Wrap them in an object.
- No Side Effects: A function shouldn’t silently change the state of the system unless that is its explicit purpose.
3. The DRY and AHA Principles
- DRY (Don’t Repeat Yourself): Every piece of knowledge must have a single, unambiguous representation within a system. Logic duplication leads to maintenance nightmares.
- AHA (Avoid Hasty Abstractions): Sometimes duplication is better than a bad abstraction. Don’t force code into a shared function before the commonalities are truly understood.
4. Comments
”Comments are always failures… because we cannot always write code that is clear enough.” — Robert C. Martin.
- Explain “Why”, not “What”: If the code is complex, explain the reasoning behind the algorithm, not what each line does.
- TODO Comments: Use these for technical debt, but ensure they are tracked.
- Warning of Consequences: Warn other developers about side effects (e.g.,
// This takes 30 seconds to run).
Clean Code Example (Refactoring)
Before: Unclean Code
// Function to handle users
function handle(u, list) {
for (let i = 0; i < list.length; i++) {
if (list[i].status === 'active') {
if (list[i].age > 18) {
// Send email
smtp.send(list[i].email, "Hello " + u.name);
}
}
}
}
After: Clean Code
/**
* Notifies all active adult users about the new update.
*/
function notifyAdultSubscribers(users) {
const activeAdults = users.filter(isAdult).filter(isActive);
activeAdults.forEach(sendWelcomeEmail);
}
function isAdult(user) {
return user.age >= 18;
}
function isActive(user) {
return user.status === 'active';
}
function sendWelcomeEmail(user) {
const subject = `Hello ${user.name}`;
emailService.send(user.email, subject);
}
5. Error Handling
- Use Exceptions, Not Return Codes: Return codes force the caller to handle the error immediately, cluttering the logic. Exceptions allow you to separate error handling from the main workflow.
- Don’t Return Null: Returning
nullforces a null check on every caller. Return an empty collection, an Optional, or use the Null Object Pattern. - Provide Context: Your error messages should capture the “what” and “where” to aid debugging.
Summary Checklist for Clean Code
- Can I understand the intent of this variable without looking at its usage?
- Does this function do exactly one thing?
- Is the error handling separated from the business logic?
- Are there any “magic numbers” that should be constants?
- Is the code itself readable enough that I don’t need these comments?