Readable Code Is Good Code, Here's How To Write It

Learn the trick to writing self documenting code


Self-documenting code is just a fancy way of saying “readable code,” and readable code is what keeps you from losing your mind at work. It's no replacement for real docs or a well placed comment, but it never hurts to write better code. Let's talk about some habits that will make your work easy to understand.

Don't use magic numbers

Look at this and tell me what it means:

What is that 23? How am I supposed to know what that is, am I Jim Carrey? That 23 is a magic number, which is a terrible name because it sounds fun, but it is not. It means that there's a number that looks important, but has no context. Always give a name for numbers:

Now it reads: if we have more students than the maximum amount allowed, do a thing. Neato. This actually leads into my next point:

Use clear variable names

I don't know why, but I used to get super self-conscious about writing long variable names. Which was dumb, because rStuNms and fStuNms are utterly terrible compared to rawStudentNames and filteredStudentNames. They seem too long, but I am telling you: after 2 weeks of not looking at the code you will not remember a single acronym. A variable name is extremely important because it's your chance to tell your reader what your code is doing:

Common abbreviations

Now because nothing in life is without caveats, there are a few common abbreviations that people use:

  • e: event
  • i, j, k: loop counters or indexes
  • arr: array
  • obj: object
  • str: string
  • num: number
  • el: element (often used with DOM elements)
  • params: parameters
  • args: arguments
  • req: request
  • res: response
  • err: error
  • cb: callback
  • temp: temporary
  • prev: previous
  • curr: current
  • max: maximum
  • min: minimum
  • avg: average
  • src: source
  • util: utility
  • ctx: context
  • msg: message
  • btn: button
  • val: value
  • prop: property
  • len: length
  • idx: index

Nothing quite like me saying "don't do something" and then giving you 25 examples when you should. I suppose the true rule is you shouldn't be making up abbreviations, but it's alright to use the standard ones.

Use naming conventions

There are a lot of conventions when it comes to naming things.

  • If your value is a boolean, start with "is" or "has," like isEnrolled
  • If your value is storing an array, the name should be plural
  • Numbers should start with min or max if possible
  • Use the singular noun for classes, eg. User, not Users
  • For functions, there should be a verb in front, like createSchedule or updateNickname

For booleans specifically, default to the affirmative whenever that's clearer. So if you only ever care of the class is empty, don't use !isFilled, use isEmpty. The rule of thumb is to use whatever variable names you need to in order to minimize the number of !s.

Refactor with tiny, named functions

Variables aren't the only places to add helpful explanations, function names are great too! When we first start writing code, we're taught that functions were mainly for DRYing out code, but I'll never forget when I had my mind blown I learned that functions really shine when they're used for readability. Look at this code for a second and tell me what's going on:

It's possible, but how about:

I'm exaggerating for effect a little, but you get it. All we did was scoot a few lines of code into functions and it improved dramatically. showSaveAlertFor is even a function we can use elsewhere if we want to. The lines of code are scooted up and away into function definitions, but you don't need to read them directly unless there's a problem. At the high level, we have a human readable chain of events. Function definitions are also another great way to label variables. setTimeout takes a function and number of milliseconds, but now we've clearly added it ourselves to be extra helpful.

Locality of behavior

Be careful not to get carried away. I don't want you to think that every 3 lines or if check needs to be abstracted away. "Locality of behavior" is the idea that the closer your logic is together, the easier it is to read. The more experience you get, the better you'll be at deciding what to break out and what to leave. The biggest clue that you may have abstracted too far is how often you have to leave the file your reading to see what a function is doing. If you find yourself jumping all over the place, you can absolutely dial back the broken out functions. It's up to you and your team to decide!

Add useful test descriptions

Probably the least talked about way of sneaking documentation into code is with tests. Suppose we have this function:

Let's pretend this is a runner function made up of a bunch of other functions, so it does a lot: It retrieves the daily schedule; if the day of the week is a weekend it returns an empty array; if the student has detention it sticks it onto the end of the schedule; and if the student isn't enrolled in the school, it prints a link to that Mean Girls Gif.

If you tried to put that paragraph as a comment, you're going to get some weird looks. But you know where that paragraph would look great? In tests:

This is hands down the easiest way to straight up put comments into code without actually adding any comments.

Bottom line: readable over clever

Being a good developer doesn't mean writing the cleverest code, it means being a good teammate. Quality software is rarely developed alone, eventually other people will need to read your code. And even if you're working alone, you-right-now and you-in-two-weeks are almost different people when it comes to remembering code. There are lots more ways to write readable code, like adding good comments, but the most important thing you can do is just start thinking about it.

Happy coding everyone,

Mike