Your Code Should Tell a Story

Practical conventions

Madhavan Nagarajan
Better Programming

--

Photo by Nick Morrison on Unsplash

Picturing writing code as being like writing a story is a helpful and easy metaphor. It’s also a wonderful perception of the way we see and write code.

I have seen in my daily life how these good practices have helped me and my team to be faster and more productive.

How readable code is structured varies with the computer language with which the programmer is transcribing their mental model into code. Nevertheless, there are a number of guidelines that can be followed in most high-level languages to help ensure code is easily readable.

Image source: “Coding like Shakespeare: Practical Function Naming Conventions

This article is all about walking you through the best practices for writing better code for our future selves and for other developers to read and adapt for code they’ve never seen before.

1. Use a Definite Name

Be super-conscious about naming variables right. Whether it’s a parameter for a function or for a global or local variable, be very specific about the name of the variable. It’s pretty common across codebases to put some shorthand names for the variables, like fname for firstName .This might look like a faster and easier way to write and deliver a piece of code, but it’s an utterly terrible idea, period.

A variable filteredStudentNames may appear to be too long, but believe me, after a while of not looking at the code, you won’t remember any of the shortened variable names. If you hire a new developer for your team, then they might need a great deal of help and time to understand the codebase.

A variable name is notably powerful because it’s your opportunity to tell your reader what your code is doing.

There are several other practices that you might know like to know about:

  • For a boolean value, start the name with has or is, e.g., isUserLoggedIn.
  • A value storing a collection name should be in the plural, e.g., Users.
  • Forget about all you’ve heard about naming. It is very important to name the variable according to what it’s supposed to do.

This rule to make names definite is also relevant to function names and class names. It’s pretty common to write a bunch of lines above the function to elaborate, but using the function name to express the indent is always underrated.

The article “Coding like Shakespeare” is a fine composition of good practices with examples, so please spare some time to read it. I have listed a few of the important ones below.

  • Prefer explanatory names — “The name should clearly, without ambiguity indicate what the function does. You don’t have to jump around searching for the truth.”
  • Avoid irrelevant function names — “Obviously, the production code should never contain functions named without strict meaning. Or named just for the sake of naming.”
  • Avoid naming functions generic — “One name that covers many/different actions in different places should be avoided. Give detailed and specific names.”
  • Don’t say one thing and do another — It is a pretty common practice to name a function as something (e.g. add), and then do one or more irrelevant actions inside it.

2. Write Small and Well-Named Functions

It’s always a habit for us to write one big function that does a lot of things at once when we are junior developers. This has created three very specific problems for me in the past.

  • I might end up with a few function-scoped variables that I have to create and track in addition to the existing global or class variables/properties.
  • It gets complicated to give a proper name to the function that defines the intent of the function.
  • I might end up hating myself when writing a unit test for those functions.
const handleSubmit = (event) => {
event.preventDefault();
NoteAdapter.update(currentNote).then(() => {
setCurrentAlert('Saved!')
setIsAlertVisible(true);
setTimeout(() => setIsAlertVisible(false), 2000);
}).then(() => {
if (hasTitleChanged) {
context.setRefreshTitles(true);
setHasTitleChanged(false);
}
});
};

The above function might look familiar. There is nothing wrong with the function itself, but writing all the logic as one giant function like this makes it more painful to read, comprehend, and test.

Rather, we could split the function into smaller discrete functions, thus making understanding and reading more straightforward. Writing small functions also lets you name the function, thereby improving the readability. That sometimes allows you to reuse them, too.

const displaySaveAlertFor = (milliseconds) => () => { 
setCurrentAlert('Saved!')
setIsAlertVisible(true);
setTimeout(() => setIsAlertVisible(false),milliseconds);
};
const updateTitleIfChanged = () => {
if (hasTitleChanged) {
context.setRefreshTitles(true);
setHasTitleChanged(false);
}
};
const handleSubmit = (event) => {
event.preventDefault();
NoteAdapter.update(currentNote)
.then(displaySaveAlertFor(2000))
.then(updateTitleIfChanged);
};

Let one function perform only one task: Do what you say you’re going to do.

3. Ditch Comments — Use Functions Properly to Convey the Purpose

Disclaimer: There is a place and a time for comments. I’m not against comments as such, just the overuse of them when clean code is a better option.

Relying on comments can lead to code that takes longer to read and digest. You should think about how your code will continue on. It’s a story that others are going to read, possibly for years to come.

Writing code for today’s problem is easy. Writing code for the future takes skill.

Sometimes we may have to use a rather unorthodox approach to solve a problem because nothing else works or we don’t have enough time to come up with a better solution. This can be hard to explain with code. Using comments throughout our code can help us fix this problem. Comments can help us explain to other people why we wrote what we wrote and why we wrote it in that specific way. As a result, other people will not have to guess.

That being said, we should use comments only when necessary, not to explain bad code. Writing endless lines of comments will not help us change poorly written code into clean code. If the code is bad, we should fix the problem by improving the code, not by adding a set of instructions on how to use it.

Clean code should take preference over the convenience of shortcuts.

Bottom Line: Readable Code Over Intelligent Code

There are several design patterns, frameworks, and libraries that you can use to aid in writing better code. But with all of these tools, and layers upon layers of abstractions and dependencies, one thing that is often forgotten is the benefit of simplicity. Keep it simple, stupid. It is one of the most fundamental and important principles, regardless of the language and framework you’re using. One of the ways to do this is by writing simple, elegant, and clean code. When done right, this code can be such a pleasure to work with that it’s almost like reading a story.

Thanks for reading. Leave any suggestions and any other good practices that you are following in your day-to-day life in the comments.

--

--