5 essential tips to writing readable code
Tags: readability, maintainability, reusablility, tips
Published: 2023-12-18 06:10:00

Code is often read than written, making code readability a crucial aspect of software development. Readable code is easy to understand, allowing developers to follow the logic effortlessly through the lines. In this article, drawing from my experience as a tutor for Rhodes University's Computer Science students, I'll share five essential tips for writing code that is not just functional but also a pleasure to read.

We will begin by looking at the following Python program

## Before 🙃

```python
def cc(s, k):
    r = ""
    for i in s:
        if i.isalpha(): 
            r += chr((ord(i) + k - 97) % 26 + 97) if i.islower() else chr((ord(i) + k - 65) % 26 + 65)
        else:
            r += i
    return r
t = "readable code?"
ct = cc(t,3)
print(f'{t} encrypted => {ct}')
```

At first glance, can you tell what it does?

> 💡 ord(char) Given a string representing one Unicode character, return an integer representing the Unicode code point of that character.
> 
> chr(i) Return the string representing a character whose Unicode code point is the integer i

Let's go! 🚀

## Whitespace: The visual guide

Whitespace is not just a formatting choice; it's a visual guide for developers. Avoid one-liner tricks that may save space but sacrifice readability. Use indentation consistently to indicate code blocks and create a visual hierarchy within your code.

```python
def cc(s, k):
    r = ""
    for i in s:
        if i.isalpha():
            if i.islower(): # good ol if statement instead of tenary
                r += chr((ord(i) + k - 97) % 26 + 97)
            else:
                r += chr((ord(i) + k - 65) % 26 + 65)
        else:
            r += i
    return r
# add line breaks between sections
t = "readable code?"
ct = cc(t,3)

print(f'{t} encrypted => {ct}')
```

## Variable Names: The art of clarity

Picking descriptive variable names is like giving your code a friendly tour guide. When names are straightforward and welcoming, your code becomes a pleasant journey for anyone reading it.

It would help if you embraced a consistent naming convention across your codebase. Whether it's CamelCase or snake\_case, be consistent to enhance readability.

Don't shy away from lengthy variable names if they accurately describe the content. A variable named `user_input` is far more informative than `u_i`. Descriptive names reduce the need for excessive comments, making your code self-explanatory.

For variables that represent a boolean value indicating true or false, it is advisable to use a naming convention that includes the "is" prefix.

```python
ALPHABET_SIZE = 26
LOWER_A_ASCII = ord('a')
UPPER_A_ASCII = ord('A')

def caesar_cipher(text, shift_key):
    result = ""
    for char in text:
        if char.isalpha():
                if char.islower():
                    result += chr((ord(char) + shift_key - LOWER_A_ASCII) % ALPHABET_SIZE + LOWER_A_ASCII) 
                else :
                    result += chr((ord(char) + shift_key - UPPER_A_ASCII) % ALPHABET_SIZE + UPPER_A_ASCII)
        else:
            result += char
    return result

text = "readable code?"
cipher_text = caesar_cipher(text,3)

print(f'{text} encrypted => {cipher_text}')
```

We also replaced our code's "magic numbers" with constants `ALPHABET_NUMBER`, `LOWER_A_ASCII`**,** and `UPPER_A_ASCII`.

## Functions: The power of modularity

Functions in your code should have a single responsibility, encapsulating a specific task. The name of a function should precisely convey what it does. Break down your code into smaller, reusable functions. This not only enhances readability but also promotes maintainability and reusability.

```python
ALPHABET_SIZE = 26
LOWER_A_ASCII = ord('a')
UPPER_A_ASCII = ord('A')

def shift_letter(char, shift_key):
    if char.islower():
        return chr((ord(char) + shift_key - LOWER_A_ASCII) % ALPHABET_SIZE + LOWER_A_ASCII)
    elif char.isupper():
        return chr((ord(char) + shift_key - UPPER_A_ASCII) % ALPHABET_SIZE + UPPER_A_ASCII)
    else:
        return char

def caesar_cipher(text, shift_key):
    result = ""
    for char in text:
            result += shift_letter(char, shift_key)
    return result

text = "readable code?"
cipher_text = caesar_cipher(text,3)

print(f'{text} encrypted => {cipher_text}')
```

## To comment or not to comment?

Comments are a double-edged sword. While they can be helpful, they are often overlooked and must be updated, leading to confusion. Prioritize writing self-explanatory code, using comments sparingly for complex algorithms or unconventional solutions.

```python
ALPHABET_SIZE = 26
LOWER_A_ASCII = ord('a')
UPPER_A_ASCII = ord('A')

def shift_letter(char, shift_key):
    '''
    "The normalization step (using LOWER_A_ASCII and UPPER_A_ASCII)
    is employed to adjust the ASCII values of letters, 
    ensuring that they are shifted within the
    range of 0 to 25, corresponding to the positions in the alphabet.
    ''' 
    # Check if the character is a lowercase letter 
    if char.islower():
        return chr((ord(char) + shift_key - LOWER_A_ASCII) % ALPHABET_SIZE + LOWER_A_ASCII)
    # Check if the character is an uppercase letter 
    elif char.isupper():
        return chr((ord(char) + shift_key - UPPER_A_ASCII) % ALPHABET_SIZE + UPPER_A_ASCII)
    else:
        # If the character is not a letter, return it unchanged 
        return char

def caesar_cipher(text, shift_key):
    result = ""
    for char in text:
        # Apply the shift to each character in the text 
        result += shift_letter(char, shift_key)
    return result

# Example usage 
text = "readable code?"
cipher_text = caesar_cipher(text,3)

print(f'{text} encrypted => {cipher_text}')
```

Our code looks cluttered because of the unnecessary comments. But, since we have used descriptive names, we should be able to trim out some comments.

## After 🙂

```python
ALPHABET_SIZE = 26
LOWER_A_ASCII = ord('a')
UPPER_A_ASCII = ord('A')

def shift_letter(char, shift_key):
    '''
    "The normalization step (using LOWER_A_ASCII and UPPER_A_ASCII)
    is employed to adjust the ASCII values of letters, 
    ensuring that they are shifted within the
    range of 0 to 25, corresponding to the positions in the alphabet.
    ''' 
    if char.islower():
        return chr((ord(char) + shift_key - LOWER_A_ASCII) % ALPHABET_SIZE + LOWER_A_ASCII)
    elif char.isupper():
        return chr((ord(char) + shift_key - UPPER_A_ASCII) % ALPHABET_SIZE + UPPER_A_ASCII)
    else:
        # If the character is not a letter, return it unchanged
        return char

def caesar_cipher(text, shift_key):
    result = ""
    for char in text:
        # Apply the shift to each character in the input string
        result += shift_letter(char, shift_key)
    return result

# Example usage 
text = "readable code?"
cipher_text = caesar_cipher(text,3)

print(f'{text} encrypted => {cipher_text}')
```

> 💡 In [cryptography](https://en.wikipedia.org/wiki/Cryptography), a **Caesar cipher** ... is one of the simplest and most widely known [encryption](https://en.wikipedia.org/wiki/Encryption) techniques. It is a type of [substitution cipher](https://en.wikipedia.org/wiki/Substitution_cipher) in which each letter in the [plaintext](https://en.wikipedia.org/wiki/Plaintext) is replaced by a letter some fixed number of positions down the [alphabet](https://en.wikipedia.org/wiki/Alphabet). For example, with a left shift of 3, D would be replaced by A, E would become B, and so on.
> 
> \-[Wikipedia](https://en.wikipedia.org/wiki/Caesar_cipher)

## Consistency is key

Consistency ties all these tips together. Adopting a consistent coding style, variable naming convention, and commenting approach across your projects promotes a seamless reading experience for anyone who encounters your code.

Some tools help you with this. Use them. When writing Python code, I use [autopep8](https://pypi.org/project/autopep8/), a tool that automatically formats Python code to conform to the PEP 8 style guide. A supporting [VS code extension](https://github.com/Microsoft/vscode-autopep8) is available.

When I'm writing 
JavaScript&nbsp;
  😅 Typescript, I use [eslint](https://eslint.org/) and [Prettier](https://prettier.io/).

In conclusion, writing readable code is not just a best practice, it's a professional courtesy to your future self and collaborators. By implementing these tips, you'll produce code that works and is a pleasure to read and maintain.