Code readability is a kind of art in itself. Unless your project is completely thrown away, it is good to get right. If you want others to understand your logic or want to revisit it another day down the road, it is important to value readable code. I am not talking about code syntax or anything that can easily be linted. I am talking about how you make your code read like a story.
This skill needs nurturing and practice that will blossom in those eager to write succinct and readable code. It is so much a skill you might spend some time and deep thought just trying to figure out what to name a variable. However, that time will benefit others who need to read your code later during review or add to it.
In fact, I’d recommend avoiding code comments in favor of well-formed readable code. Your logic can literally read like a spoken description with minimal code comments if you become skilled enough.
Guidelines to follow for code readability
Structure
- Have one output per assignment. Avoid chaining together loop statements into one statement. With great power comes great responsibility! Too many loops chained back to back that it is near impossible to follow. Please keep it simple: one loop that returns a new output per variable assignment. The variable should ideally read and describe the output.
- Keep a continuous execution block of code small. I won’t name a limit for exactly how long your code blocks should be. If you have one block of code running over 100 lines, you likely have gone too far. Things read better when you encapsulate complexity into functions.
- Have blocks of code represent just one thing. When you design your code, functions should inherently do one thing and do that well, whether it describes at a high level a series of actions to specific actions themselves. A simple manifest to a detailed action should describe one thing that it is doing in logical execution. Each action should do just one thing.
- Limit the number of characters per line. Smashing everything together to save code lines and using lengthy single line statements will force the reader to scroll horizontally. This will impact readability. Think of a book; there are only so many characters per line you can put in there. Make your code read like a book.
Semantics
- Name your variables to read like the intent of the assignment. If you’re combining two arrays, don’t make a variable be called “array” or “list.” Make it specific to what it represents. For example, if you’re combining a list of zebra objects with a list of lion objects, the variable could be “lionAndZerbaList.” Variables that describe what they hold can make logic read like a story, which is excellent!
- Focus on not writing comments first. Comments are good when the code is complex. Having no comments with highly readable code is better. Always favor no comments and readable code first. Comment if needed to record some quirk discovered that doesn’t match the semantics of the code. Avoid writing comments for something that can be read obviously from the code unless it provides more clarity.
- Avoid shortening words and abbreviations. Use the build system to save text and compile the code into something smaller. The code checked in should be highly readable, and using too many shortened words or abbreviations makes things less understandable.
- Const correctness. Use const correctness and keywords in your programming language to represent your intention (if the language supports it). Further, the compiler can understand your plans and enforce specific rules at compile time. Const variables make the reference immutable and specify to the reader it is not intended to change.
Example of readable code
Note, the logic below doesn’t really mean anything useful; it is only here to portray the example.
// Hard to read block of code
function calc (c) {
// check the args
var data = c.arg.filter(a => a.key.endsWith('Data') || a.key.startsWith('Point')).map(a => a.value).forEach(a => Math.round(a))
// only process if we have data
var ret
if (data && data.length > 0) {
ret = data.reduce((a, v) => a + (v > 5 ? v * 2 : v < 1 ? v / 2 : v))
}
return ret
}
// Same exact logic above, no changes to performance or handling. Simply
// improvements over readability.
function calculateDataBreakPointFromConfiguration (configuration) {
const filteredDataPointList = configuration.arg.filter(_isADataPoint)
const extractedValueList = dataPointList.map(a => a.value)
const roundedValueList = extractedValueList.forEach(a => Math.round(a))
return _calculateBreakPointOnDataSet(roundedValueList)
}
function _isADataPoint (argument) {
return argument.key.endsWith('Data') || argument.key.startsWith('Point')
}
function _calculateBreakPointOnDataSet (dataSet) {
if (dataSet && dataSet.length > 0) {
return data.reduce(
(accumalator, value) => accumulator + _calculateBreakPoint(value)
)
}
// If data is invalid we default to zero as a safety value
return 0
}
function _calculateBreakPoint (breakPoint) {
if (breakPoint > 5) {
return breakPoint * 2
} else if (breakPoint < 1) {
return breakPoint / 2
} else {
return breakPoint
}
}
Avoid comments (most of the time)
I generally avoid long-winded comments. However, I do not want to polarize the usage of commenting code to something like “there shall be no comments.” I believe code comments should only be used in the following circumstances.
- Public APIs for a library or a system meant to be used by others. Ideally, on interfaces. An API should aim to be used as a contract and understood as an accessor to specific actions. Comments go a long way for APIs as documentation that lives with the code. Most IDEs today will even help others trying to use the APIs by showing the comments with tooltips.
- Mathematical or complex constructs. Anything that you look at and think to yourself… what is this? Sometimes sheer readable code is not enough; a comment goes a long way to seed the complexity enough for maintainability. Some may argue this could still be simplified but comment it if you can’t simplify it. These should be rare.
- Some quirk or workaround. Too many times, I’ve seen others write logic that looks incorrect. Finding the code is a hack or unobvious workaround to a quirk in a library that caused a regression. These need comments. Be explicit that this code should not be changed, and the oddity is intentional if it doesn’t logically read well and make sense at first glance, comment on it.
Benefits of high code readability
Code readability yields several benefits to you and your team. Software products have code that will need to be reviewed or revisited as business drives the need for change.
- A low-cost solution for regression reduction (in comparison to high coverage of units or functional tests). While your coding, focusing on readable code makes it easy to understand the code’s intent and thus will reduce regression risk. This doesn’t replace automated or manual testing, but understanding the code’s intent goes a long way.
- Forces low complexity code. When complexity gets too high, and you find yourself with tons of if statements or looping going on, you’ll find the code looks complex and is not readable. Taking a bit of time to break it up and make it readable will reduce the complexity significantly.
- Forces singular concerns. It’s pretty hard to make code readable if functions or chunks of the code are doing multiple things. You’ll find breaking code up naturally improves your code’s readability. It will force certain sections of it to isolate logic to do one thing and one thing only. This is a great practice in and of itself.
For more thoughts on improving code design for yourself or your team, refer to this article to understand the importance of code design.