The 3 Laws of Writing Readable Code: A Guide to Clean Programming
Learn how to transform messy, hard-to-maintain code into clean, professional-grade solutions. Discover three fundamental laws that distinguish expert developers from beginners, with practical examples you can apply today.
Writing code that others (and your future self) can easily understand is a crucial skill that separates experienced developers from beginners. In this guide, we'll explore three fundamental laws that will help you write more readable and maintainable code.
Law 1: Avoid Deep Nesting
Deep nesting is often a telltale sign of inexperienced programming. When code is heavily nested, readers need to hold multiple conditions in their mind while trying to understand the core logic. Let's look at a before and after example:
Before (Deeply Nested Code):
function calculateTaxes(user, cart) {
if (!isMaintenancePeriod) {
if (user.isAuthenticated) {
if (user.isAuthorized) {
if (cart.total > 0 && cart.items.length > 0) {
switch (user.taxRegion) {
case 'US':
return cart.total * 0.08;
case 'EU':
return cart.total * 0.20;
default:
return 0;
}
}
}
}
} else {
return null;
}
}
After (Improved Code):
function calculateTaxes(user, cart) {
if (isMaintenancePeriod) {
return null;
}
if (!isValidUser(user)) {
return null;
}
if (!isValidCart(cart)) {
return null;
}
return calculateRegionalTax(user, cart);
}
function isValidUser(user) {
return user.isAuthenticated && user.isAuthorized;
}
function isValidCart(cart) {
return cart.total > 0 && cart.items.length > 0;
}
function calculateRegionalTax(user, cart) {
switch (user.taxRegion) {
case 'US':
return cart.total * 0.08;
case 'EU':
return cart.total * 0.20;
default:
return 0;
}
}
Key improvements:
Used early returns to reduce nesting
Extracted complex conditions into well-named functions
Separated concerns into smaller, focused functions
Made the code's intent clear at each step
Law 2: Avoid Code Duplication
Duplicated code is a maintenance nightmare. When similar logic appears in multiple places, making changes becomes risky and time-consuming. Here's an example:
Before (With Duplication):
function getUserProfile(id) {
const cacheKey = `user_${id}`;
const cachedData = cache.get(cacheKey);
if (cachedData) {
response.writeHead(200, { 'Content-Type': 'application/json' });
response.end(JSON.stringify(cachedData));
return;
}
const userData = database.fetchUser(id);
cache.set(cacheKey, userData);
response.writeHead(200, { 'Content-Type': 'application/json' });
response.end(JSON.stringify(userData));
}
function getTeamProfile(id) {
const cacheKey = `team_${id}`;
const cachedData = cache.get(cacheKey);
if (cachedData) {
response.writeHead(200, { 'Content-Type': 'application/json' });
response.end(JSON.stringify(cachedData));
return;
}
const teamData = database.fetchTeam(id);
cache.set(cacheKey, teamData);
response.writeHead(200, { 'Content-Type': 'application/json' });
response.end(JSON.stringify(teamData));
}
After (Without Duplication):
function getUserProfile(id) {
return getCachedData(
`user_${id}`,
() => database.fetchUser(id)
);
}
function getTeamProfile(id) {
return getCachedData(
`team_${id}`,
() => database.fetchTeam(id)
);
}
function getCachedData(cacheKey, fetchData) {
const cachedData = cache.get(cacheKey);
if (cachedData) {
return sendJsonResponse(cachedData);
}
const freshData = fetchData();
cache.set(cacheKey, freshData);
return sendJsonResponse(freshData);
}
function sendJsonResponse(data) {
response.writeHead(200, { 'Content-Type': 'application/json' });
response.end(JSON.stringify(data));
}
Key improvements:
Extracted caching logic into a reusable function
Separated response handling into its own function
Made the code more maintainable and DRY (Don't Repeat Yourself)
Reduced the risk of inconsistent implementations
Law 3: Use Clear and Meaningful Names
Cryptic variable and function names make code impossible to understand. Always use descriptive names that clearly convey purpose and intent.
Before (Poor Naming):
function calc(a, b) {
let res = 0;
let tmp = a;
for (let i = 0; i < b; i++) {
res += tmp;
}
return res;
}
After (Clear Naming):
function multiply(multiplicand, multiplier) {
let product = 0;
let currentValue = multiplicand;
for (let count = 0; count < multiplier; count++) {
product += currentValue;
}
return product;
}
Key improvements:
Used descriptive function name that explains the purpose
Chose variable names that reflect their roles
Made the code self-documenting
Enhanced readability without adding comments
Conclusion
Following these three laws will dramatically improve your code's readability:
Avoid deep nesting by using early returns and extracting complex logic
Eliminate code duplication by creating reusable functions
Use clear, meaningful names that explain purpose and intent
Remember, code is read far more often than it is written. Taking the time to make your code readable will save countless hours for both you and your fellow developers in the long run.