Code Should Tell a Story

The key to writing maintainable code that doesn’t make future developers want to scream at you is to follow a simple rule: “Code should tell a story”

 

What defines that?

The code should be immediately understandable.
Anyone in the future who jumps into the project should be able to look at your code and immediately understand what the intended behavior is.

 

Why is this important?

A lot of companies place emphasis on KPIs, but these goals make bad developers. Non-engineers cannot create metrics to measure the performance of engineers. Writing more lines of code, reducing lines of code, finishing tasks faster…. none of these result in good code that tells a story. By measuring more lines of code, you get junk. By measuring lines of code reduced, you get convoluted nonsense. And meeting deadlines means garbage code and engineers who focus on their own tasks rather than improving their skills and helping team members.
If your code tells a story, you are planning for the future. Developers who join the project after the original authors have left the company will not only be able to contribute faster, but they will do so without being reliant on legacy knowledge.

 

How can we achieve this?

Stop using so many abbreviations

Muh Boi, this quality is what all true developers strive for


What you consider to be correct may not what others believe to be correct. Because abbreviations differ from person to person, it makes them unreliable. One person may think that Rakuten is abbreviated “rkten”, whereas another person thinks “rktn” is the right way to abbreviate it. Well, both are wrong, because the word isn’t typically abbreviated. It’s written out as Rakuten. Also, it’s 2018, we aren’t concerned about saving bytes, and on top of that you should be minifying your code or compiling it anyways. Abbreviating things accomplishes nothing and only has detriments associated with it.

In addition to not knowing the “correct” abbreviation, there can be misinterpretations. Consider the following 4:

sry
btch_fkr
cnt
md

What does your mind think these are? The correct answers are below:
sry is “stamp rally” – Yeah, thanks Japan. That’s not what comes to mind when I see “sry”, but what I encountered in our code.
btch_fkr is “batch faker” – Unbelievable. Something written to perform a dry run for a batch which was written outside of any testing framework.
cnt is “count” – Of course. What else could it be? This one appears a lot.
md is not something that I encountered in the code, but rather something used to illustrate a point. It’s an abbreviation that is immediately recognizable by a large portion of the world, and completely unknown to another large portion of the world. Are you from the part of the world where the meaning is immediately obvious? It’s not MiniDisc or medical doctor…

If it’s a word that is typically abbreviated and known throughout the world, like ID, ATM, or DVD, you can abbreviate it if you wish so long as you are 100% consistent throughout all of the code.

Sometimes it makes sense to use abbreviations in lambda expressions. That’s acceptable.

 

Generic variable names

Stop it. Just stop.
Every variable exists in memory and is temporary. There is no need to name these “temp”, “tmp”, “var”, “val”, “ary” or any other nonsense.
These add nothing to the code, and instead of telling a story you are now writing algebra, forcing future coders to spend more time deciphering what you wrote.
The variable names should reflect what is stored in the variable.

function calculateTaxes(val) {
  const tax1 = 0.08
  const tax2 = 0.1
  const var = 10000
  const tmp = val * tax1
  if (tmp > var) {
    tmp = tmp * tax2
  }
  return tmp
}

Let’s rename things to show more information

function applyTax(price) {
  const consumptionTax = 0.08
  const luxuryTax = 0.1
  const luxuryTaxThreshold = 10000
  const subtotal = price * consumptionTax
  
  if (subtotal > luxuryTaxThreshold) {
    return subtotal * luxuryTax
  } else {
    return subtotal
  }
}

The exception to this would be variables in lambda expressions, which can be small abbreviations for the sake of saving space.

taxAppliedTotals.map(taxAppliedTotal => taxAppliedTotal * consumptionTax)
taxAppliedTotals.map(x => x * consumptionTax)

 

Better variable and function names

This is a tricky aspect to writing better code because it can be so subjective. A “better name” for one person may not be a “better name” for another person, so pull requests and active code reviews are vital to the long-term success of the code. Get everyone’s eyes on the code and choose the best words that represent the data, functions, and whatever else you are working with. Additionally, when working in an international environment, you cannot always push for the best translation, because that word may not be known to future developers. On the flip side, there may be some industry-specific terms that should remain as-is, and be learned by the people new to the team.

You want to make the names short, but at the same time convey all of the relevant information as to what they store or do.

Once a name has been chosen, keep it standardized…. or standardised! The important thing is that you make a choice and stick to it until the team is willing to change. Are you using “cancel”, “cancellation”, or “withdrawn” everywhere in the code? Are you using “billings” or “payments”? Are you using “regular” with “solo” or “group” with “solo”? Choose the right word, and use it everywhere.

 

Shorter names

Unless you are doing Objective-C or Java, you’ll have the capability of writing variable names that aren’t complete sentences.

There’s descriptive… and then there’s too long, but this is another thing that is subjective – It’s up to you and your team to define what is “too long”.
Hopefully you have established a limit to the number of characters to a line, and shorter names will make it easier to fit more information on each line.

 

Much fewer comments

If you have good code, the code doesn’t need comments.

“Oh hold on, this is crazy! No comments!? Comments tell future developers what the code is doing!”

If you have a good story, the story doesn’t need notes.

I completely understand that this is the programming equivalent of insulting somebody’s religion, and this is often a point of disagreement, but I’ve worked on several codebases of varying quality and now push for as few as comments for possible in my projects.

Code isn’t always perfect, and there will inevitably be situations where comments are required. But remember that comments refer to code, and adding comments to code isn’t DRY. Comments are copied code, and if the code changes you need to update the comments. If you don’t update a comment when code changes or vice verca, it leads to undue confusion in the future as developers need to investigate where the problem lies.

The difficulty and feasibility of removing all comments depend on the language. With Ruby, I found that it was quite easy to survive without comments. For larger jQuery files, comments become more valuable as code for events would exceed the size of the screen.

Some people have argued that comments at the heads of files are important. I politely and respectfully think the complete opposite is true. The information that appears is typically information that can be found elsewhere, but if it is a comment it means that it will need to be updated.

  • Comment contains author’s name – How do you determine the author? File creator? A person who created the most content? This is pointless; we have `git blame` which shows who wrote what and when. (EDIT: It’s been pointed out to me that `git blame` doesn’t help if someone indents that code or moves that code! Good point!)
  • Comment contains updated date – Pointless. `git blame` and `git log`
  • Comment contains file’s purpose – This should be gleaned from folder structure and the name of the file.

 

Get rid of the clutter

Too often I find a bunch of junk in the code that doesn’t need to be there. Extra junk convolutes the story you want to tell, and sometimes even reduces performance.
We want to reduce line count to get more relevant information in our viewport and get a bigger understanding of the code. Here’s an in-depth ~rant~ look

In short; use ternary operators, no junk variables, use all the cool tricks available to you in the language

A lot of what you can do depends on the language, but you be surprised at all of the features your language offers that lets you write fewer lines of code

if (myExample == true) {
  return false;
} else { 
  return null;
}

5 lines for something basic? Use a ternary operator

return myExample ? false : null
if (myExample === null) {
  return 'abc'
}

Checking if it’s null? This can be one line in a lot of languages

return myExample || 'abc'

 

function exampleFunction(loopNumber) {
  if (loopNumber === null) {
    loopNumber = 3;
  }

  returnArray = [];
  for (i = 0; i < loopNumber; i++) {
    returnArray.push({index: i, value: i + 1});
  }
  return returnArray;
}

Use default arguments and make use of cool things like .map(), .reduce(), .filter(), .forEach() (name may vary by language. Other aliases and variations are “each”, “collect”, “find”, “inject” and more)

function exampleFunction(loopNumber = 3) {
  // JS has a few ways to do this. This is one: return Array.from({length: loopNumber}, (v, i) =&gt; {return {index: i, value: i + 1}})
  // Ruby is beautiful: (0...loopNumber).map{|x| {index: x, value: x+1}}
  // PHP is (maybe) this: array_map(function ($x) { return ["index" =&gt; $x, "value" =&gt; $x + 1]; }, range(0,3)); 
  // Python has: map(lambda x: {'index': x, 'value': x+1}, range(0, 3))
  // The point is: You don't need to write out your own loops. These things can be done on one line with any number of available iterative functions
}

 

Triple equals

There was a time when loosely-typed languages were great for web development. Variables were passed between pages in GET requests, plain for the world to see (and tamper). Validation was minimal and would take the form of critical, catastrophic, site-breaking errors.
Triple-equals should be used it nearly all comparison situations. In PHP, === is about 6 times faster than == due to it not doing any type-juggling.
In addition to the performance benefits, your code is very explicit in what types of data are expected. Some might say….it tells a story™! Truthy-falsely things can change depending on language, and people come from many different programming backgrounds, so it’s best not to leave ambiguous parts of code in your codebase.

 

Use code linters and set rules

Linters help fix simple issues, written rules help resolve complicated issues. What you have in your head and what you may consider being basic knowledge may not be in other people’s heads. It’s better to be explicit in what you consider to be quality code, and document that in Confluence or a wiki, then referring people to that.

 

Give hints at the return type

Are you returning a boolean? Prefix functions/methods with “is” or “has”.

Are you using Ruby or a language that lets you use ‘?’ and ‘!’ in names? If so, make use of those. If the function/method returns boolean, append ‘?’. If it does something like modifying data in place, use ‘!’

If you are using something with computed props, name those the same way you would name variables.

 

Keep it DRY

No excuses; If the same thing appears twice, it’s probably wrong. If there are sections of copy-pasted code across files in the same repo, it is almost always wrong. If there are sections of copy-pasted code within the same file then it is completely wrong and embarrassing.

The reason we keep code DRY is that it makes the code more understandable. You just need to find the one place that you need to edit in order to make changes. Imagine fixing a bug and pushing it to production only to find out that there is a nearly-identical codebase for the mobile site that you also need to perform emergency edits on because nobody bothered to tell you because nobody knew because the code is so garbage that all of the good developers left the team and all the bad ones became managers and I really need to stop sharing so much about my past experiences.
DRY reduces workload – you make changes to one place… not 2, or 3, or 4, or n.

This is a core programming principle and not opinion. You must write DRY code. If you see someone copy-pasting something, don’t let them merge that content. But despite this being a core concept, it’s easily forgotten by a number of developers.

Disagree with anything mentioned here? Experience a situation that made something written here impossible? Want to tell me how wrong I am by not wanting comments in the code? Leave a comment below and let’s discuss it.

One thought on “Code Should Tell a Story

  1. > Comment contains author’s name – How do you determine the author? File creator? Person who created the most content? This is pointless; we have `git blame` which shows who wrote what and when.

    I 99% agree with this, but there’s one problem that I’ve come across a few times where “git blame” shows the last person who touched the code but not who wrote it. So, I sometimes get asked, “Why did you write this obviously garbage code?” and I look at it and go, “… I didn’t write this. I just moved the existing model to the submodule.”

    Of course, we could figure out who originally added the file from github/stash, but then we also have cases where the code was moved from a different repository…

Leave a Reply

Your email address will not be published. Required fields are marked *