It’s often said that reading code is harder than writing it, but is it really? If that were the case for books, everyone would be writing their own rather than reading the books others wrote. But, a page of code isn’t equivalent to a page of text. Code, by nature, is denser. To effectively explain what a page of code does may easily take several pages, and in some cases a lot more.
Is it possible to make code easier to read than to write? After all, code is read more often than it is written when multiple developers work on the same codebase. If developers spend less time to understand how the existing code works, they can start writing code faster.
In his book Code Complete, Steve McConnell describes a process called pseudocode programming that starts with writing pseudocode where each line, written in English, describes a specific operation in relatively high level. You then turn the pseudocode into comments, and finally fill in the code below each comment. Here is an example in JavaScript:
async function createUser(email, password, firstName, lastName) {
// Validate input fields, and return error if validation fails
...
// Check if the password conforms to the password policy, if not return error
...
// Check the user table to see if email is available, if not return error
...
// Hash user password by using a strong hashing algorithm
...
// Create a new record in the user table, return user's id on success or return error
...
}
Though it lacks a few details, I don’t think any developer would have a problem understanding the above pseudocode. Comments also come handy in AI powered code generation tools to auto generate some of the code.
Can we go one step further, and actually write code that looks like pseudocode? By using a variation of the Chain of Responsibility pattern, it’s possible to write code that appears remarkably similar to pseudocode:
const chain = require('fn-one');
// (...)
async function createUser(email, password, firstName, lastName) {
return await chain(
() => validateInput(email, password, firstName, lastName),
() => isPasswordValid(password),
() => userService.isEmailAvailable(email),
() => userService.hashPassword(password),
(hashedPassword) => userService.create(email, hashedPassword, firstName, lastName)
);
}
For chaining functions, I used fn-one, a tiny library I wrote, but there are quite a few function chaining libraries out there. It doesn’t matter which one you use, what matters is, whenever possible, to call one function per line. This style of coding may require you to write a lot of small functions, but the code can become significantly easier to follow as a result.
When chaining functions, each function’s output is passed to the next function in line. If a function returns false or throws an exception, the chain breaks. There are no explicit control structures such as if-else statements and loops that clutter the code. They are hidden inside lower-level functions where they belong.
Related: