How to Write Clean Code – Tips for Developers with Examples
Argho Dev
Posted on November 30, 2024
Writing clean code is an essential skill for developers. Clean code not only makes your code easier to read, maintain, and debug but also ensures that other developers can collaborate effectively. Whether you're a beginner or a seasoned developer, following clean code principles is crucial for creating robust and efficient software.
In this blog, we’ll explore tips for writing clean code with examples and actionable insights.
What is Clean Code?
Clean code is:
- Readable: Easy for others (and your future self) to understand.
- Maintainable: Simple to modify or extend without breaking functionality.
- Efficient: Solves the problem without unnecessary complexity.
- Testable: Designed in a way that facilitates testing and debugging.
Let’s have a look at this graph. It shows two different ways of writing code and how they affect the time it takes to add more lines:
💀 Quick & Dirty Code (Red line): This is when you write code quickly without planning or organizing it well. At first, it may seem faster, but as more lines are added, it becomes harder to understand and fix. So, over time, it takes longer and longer to add each new line.
🌩 ** Thoughtful & Clean Code** (Blue line): This is when you write code carefully, making it easy to understand and change. At first, it might take a bit longer, but over time, it remains easy to work with. This way, adding new lines doesn't become more difficult.
1. Use Meaningful Names
Tip: Choose descriptive and unambiguous names for variables, functions, and classes.
Example (Bad):
let a = 10;
function d(x) {
return x * a;
}
Example (Good):
let taxRate = 10;
function calculateTax(amount) {
return amount * taxRate;
}
🎯 Naming Tips
-
Variables: Use nouns that describe the data, like
userAge
ortotalAmount
. -
Functions: Use action words, like
calculateTotal()
orfetchUserData()
. -
Classes: Use singular nouns, like
User
orOrder
, to represent what they are.
2. Follow the Single Responsibility Principle
Tip: Each function or class should have one specific purpose.
Example (Bad):
function handleUserInput(input) {
validateInput(input);
saveToDatabase(input);
sendNotification(input);
}
👎 Why this is bad:
The function name calculateTotalAndReturnRecord shows that it’s trying to do multiple things. If you want to use just the calculation, you can’t reuse this function without the record part. It’s also harder to update and test each task separately.
Example (Good):
function validateInput(input) {
// validation logic
}
function saveToDatabase(input) {
// database logic
}
function sendNotification(input) {
// notification logic
}
// Call these functions separately.
👍 Why this is good:
Each function has a clear, focused task. calculateTotal only does the math, while createCalculationRecord adds the extra details. If you want to change how the total is calculated, you only update calculateTotal, and if you want to change the record format, you only update createCalculationRecord.
3. Write Smaller Functions
Tip: Break large functions into smaller, more manageable ones.
Example (Bad):
function processOrder(order) {
// Validate order
if (!order.items || order.items.length === 0) {
console.log('Invalid order');
return;
}
// Calculate total
let total = 0;
for (let item of order.items) {
total += item.price;
}
// Apply discount
if (order.coupon) {
total -= total * order.coupon.discount;
}
// Save order
saveOrderToDatabase(order);
}
Example (Good):
function validateOrder(order) {
return order.items && order.items.length > 0;
}
function calculateTotal(order) {
let total = order.items.reduce((sum, item) => sum + item.price, 0);
if (order.coupon) {
total -= total * order.coupon.discount;
}
return total;
}
function processOrder(order) {
if (!validateOrder(order)) {
console.log('Invalid order');
return;
}
const total = calculateTotal(order);
saveOrderToDatabase(order);
}
4. Write Smaller Functions
🎯Tip:
- Break large functions into smaller, more manageable ones.
- Smaller functions are easier to read, test, and reuse.
Example (Bad):
function processOrder(order) {
// Validate order
if (!order.items || order.items.length === 0) {
console.log('Invalid order');
return;
}
// Calculate total
let total = 0;
for (let item of order.items) {
total += item.price;
}
// Apply discount
if (order.coupon) {
total -= total * order.coupon.discount;
}
// Save order
saveOrderToDatabase(order);
}
Example (Good):
function validateOrder(order) {
return order.items && order.items.length > 0;
}
function calculateTotal(order) {
let total = order.items.reduce((sum, item) => sum + item.price, 0);
if (order.coupon) {
total -= total * order.coupon.discount;
}
return total;
}
function processOrder(order) {
if (!validateOrder(order)) {
console.log('Invalid order');
return;
}
const total = calculateTotal(order);
saveOrderToDatabase(order);
}
5. Use Consistent Formatting
Readable code uses indentation, line breaks, and spaces to keep everything neat and organized. Think of it like writing a story: paragraphs make reading easier by breaking up large chunks of text. In coding, line breaks serve the same purpose.
Example (Bad):
if(isActive){
console.log("Active");
}else {
console.log("Inactive");
}
Example (Good):
if (isActive) {
console.log("Active");
} else {
console.log("Inactive");
}
In VS Code, Prettier and Black are popular formatters that automatically apply clean code styling for multiple languages.
PyCharm and IntelliJ have powerful built-in formatters with customizable rules, supporting PEP 8 for Python and other standard guides. These tools ensure consistent, readable code across projects with minimal manual effort.
6. Be Careful with Dependencies
🔌 Dependencies are pieces of software that your code relies on.
Imagine you're building a web app that sends emails. Instead of writing the email-sending code yourself, you use an external library like Nodemailer.
Here, Nodemailer is a dependency --- your app relies on it to handle the email-sending functionality.
Example:
const nodemailer = require('nodemailer');
function sendEmail(to, subject, message) {
const transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: 'your-email@gmail.com',
pass: 'your-email-password'
}
});
const mailOptions = {
from: 'your-email@gmail.com',
to: to,
subject: subject,
text: message
};
return transporter.sendMail(mailOptions);
}
In this code, nodemailer is imported and used to create a transporter for sending emails. Without it, you’d need to build all the email functionality from scratch, which would be complex and time-consuming. By using Nodemailer as a dependency, your app can send emails easily.
Even though dependencies are useful, you should try to avoid over-dependence on external software or libraries. Use dependencies only when they simplify your work or add important functionality.
🎯 Managing dependencies effectively is key to writing clean code. Here are some tips:
- Limit Dependencies: Only include libraries or modules that are essential for your project.
- Keep Versions Updated: Use updated versions of libraries to avoid security risks.
- Separate Logic: Write core functions yourself whenever possible. This way, if you ever need to remove a dependency, it won’t break your code.
7. Avoid Magic Numbers and Strings
👌*Tips: *
- Named constants make the code more understandable and reduce errors during updates.
- Replace hard-coded values with named constants or enums.
Example (Bad):
if (user.role === 1) {
console.log('Admin');
}
Example (Good):
const ROLES = {
ADMIN: 1,
USER: 2,
};
if (user.role === ROLES.ADMIN) {
console.log('Admin');
}
8. Organize Your Project
A well-organized project structure is as important as the code itself.
Think of this like organizing your workspace—you need designated places for everything so that you can find them easily. For coding projects, create folders for specific parts, like components, utils, and services.
📂 How to Organize Your Project
To set up a clean and organized project, you should categorize different parts of your code into designated folders. Here’s a simple example of what a well-organized project structure might look like:
├── src
│ ├── components
│ ├── services
│ ├── utils
└── tests
9. Avoid Hardcoding Values
Hardcoding is directly embedding data values in code, like setting a user ID as 123 instead of using a variable.
Avoiding hardcoded values allows you to reuse code without making constant changes. Store values in variables, constants, or configuration files instead.
Here’s a scenario where hardcoding can lead to issues:
// Bad: Hardcoding user limit
function createUser(name) {
let numberOfUsers = 100; // Hardcoded value
if (numberOfUsers >= 100) {
return 'User limit reached.';
}
// Code to create the user
return 'User created.';
}
In this example, numberOfUsers is hardcoded to 100. If you want to change the user limit, you have to find and modify this value in the code. If it appears in multiple places, this task becomes cumbersome and error-prone.
🏭 Improved Example Using Constants
Now, let’s refactor this code to use a constant instead:
// Good: Using a constant
const MAX_USERS = 100; // Store the limit in a constant
function createUser(name) {
let numberOfUsers = getCurrentUserCount(); // Get the current count from a function or database
if (numberOfUsers >= MAX_USERS) {
return 'User limit reached.';
}
// Code to create the user
return 'User created.';
}
// Example function to get current user count
function getCurrentUserCount() {
// Simulate fetching the current count, e.g., from a database
return 90; // Example count
}
10. Write Clear Comments
*Tip: *
- Unnecessary comments clutter the code, while meaningful comments provide valuable context.
- Use comments to explain the "why," not the "what." Example (Bad):
// Increment i by 1
i++;
Example (Good):
// Ensure the loop continues by incrementing the counter
i++;
11. Use Proper Error Handling
*Tip: *
- Robust error handling prevents crashes and improves user experience.
- Anticipate failures and handle them gracefully.
Example (Bad):
let user = getUserById(id);
// Assume user always exists
console.log(user.name);
Example (Good):
let user = getUserById(id);
if (user) {
console.log(user.name);
} else {
console.log('User not found');
}
12. Write Unit Tests
Tip:
- Tests catch bugs early and provide a safety net for future changes.
- Test individual pieces of code to ensure reliability.
Example:
function add(a, b) {
return a + b;
}
// Unit test
describe('add', () => {
it('should return 5 for add(2, 3)', () => {
expect(add(2, 3)).toBe(5);
});
});
13. Avoid Code Duplication
*Tip: *
- Reusing code reduces errors and makes updates easier.
- Reuse code wherever possible by creating reusable functions or modules.
Example (Bad):
function calculateCircleArea(radius) {
return Math.PI * radius * radius;
}
function calculateCylinderVolume(radius, height) {
return Math.PI * radius * radius * height;
}
Example (Good):
function calculateCircleArea(radius) {
return Math.PI * radius * radius;
}
function calculateCylinderVolume(radius, height) {
return calculateCircleArea(radius) * height;
}
14. Keep It Simple
🎯 Tip:
- Simpler code is easier to understand, test, and maintain.
- Avoid overengineering or adding unnecessary complexity.
Example (Bad):
function calculateSquare(num) {
return Math.pow(num, 2);
}
Example (Good):
function calculateSquare(num) {
return num * num;
}
✔ Best Practices for Clean Code
Now that we’ve covered some important tips, let’s look at some overarching principles that make up the philosophy behind clean code:
🎏 Simplicity: Always aim to make your code as simple as possible.
🧂 Consistency: Keep your code uniform in style and structure.
🌾 Clarity: Your code should clearly communicate what it does.
⛽ Efficiency: Write code that’s optimized for performance without sacrificing readability.
These principles make coding less about writing and more about designing solutions. Writing clean code is a skill that grows with practice, so keep learning and improving over time.
Conclusion 🏁
Writing clean code is like building a strong foundation for a house. It keeps everything in order, making it easy to add new features or fix issues as your project grows.
With these tips, you can start developing habits that will make your code more readable, maintainable, and enjoyable to work on.
🙋♂️ Hope to see you next time!
Posted on November 30, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.