Code Readability: An Overview

Code Readability: An Overview

Code Quality and Maintenance

Programs must be written for people to read, and only incidentally for machines to execute. — Harold Abelson, Structure and Interpretation of Computer Programs

Maintenance takes up about 70% of the total software lifecycle and a large portion of the overall costs. This means that we spend more time reading than we do writing code. When code is easy to understand, it is also easy for other developers to efficiently analyse, debug, and extend.

This article explores readability as a means for improving code quality and maintenance.

What Is Readability?

According to this research by Todd Sedano, it takes almost 4 times as long to review hard-to-read code. However, an encouraging two-thirds of the programmers surveyed wrote easy-to-read code by default. To improve readability across the board, we first need to understand what it is.

Readability is a means of assessing code quality based on factors such as the:

  • Formatting of code, including indentation, spacing, letter case, etc.

  • Naming of variables, functions, classes, etc.

  • Nesting levels of conditionals and loops

  • Complexity of a function, including its length and the number of parameters it accepts.

These are not the only factors to consider but they provide a good starting point for assessment. Readability is considered one of the key metrics of software quality, and it is probably the most straightforward way to evaluate and improve code quality.

Example Guidelines to Improve Readability

The following code snippets are in Ruby but a majority of the guidelines are transferable to other programming languages. Let's dive in!

1. Use two spaces per indentation level

# hard
def some_method
    do_something
end

# easy
def some_method
  do_something
end

2. Write one expression per line

# hard
puts 'foo'; puts 'bar'

# easy
puts 'foo'
puts 'bar'

3. Use a space before and after operators and after commas, colons, and semicolons

# hard
sum=1+2
a,b=1,2
class FooError<StandardError;end

# easy
sum = 1 + 2
a, b = 1, 2
class FooError < StandardError; end

4. Leave an empty line between function definitions

# hard
def some_method
  do_something
end
def some_method
  do_something
end

# easy
def some_method
  do_something
end

def some_method
  do_something
end

5. Do not use space for method calls and bracket access

# hard
print (x + y)
some_collection [index_or_key]

# easy
print(x + y)
some_collection[index_or_key]

6. Leave an empty line between method calls

# hard
def some_method
  data = initialize(options)
  data.manipulate!
  data.result
end

# easy
def some_method
  data = initialize(options)

  data.manipulate!

  data.result
end

7. Do not use empty lines around class, function, and other built-in statement bodies

# hard
class Foo

  def foo

    begin

      do_something do

        something

      end

    rescue

      something

    end

  end

end

# easy
class Foo
  def foo
    begin
      do_something do
        something
      end
    rescue
      something
    end
  end
end

8. Align or indent by two spaces, the arguments of a method call if they span more than one line

# hard
def send_mail(source)
  Mailer.deliver(
      to: 'bob@example.com',
      from: 'us@example.com',
      subject: 'Important message',
      body: source.text)
end

# easy
def send_mail(source)
  Mailer.deliver(to: 'bob@example.com',
                 from: 'us@example.com',
                 subject: 'Important message',
                 body: source.text)
end

# easy
def send_mail(source)
  Mailer.deliver(
    to: 'bob@example.com',
    from: 'us@example.com',
    subject: 'Important message',
    body: source.text
  )
end

9. Avoid the use of nested conditionals for control flow

# hard
def compute_thing(thing)
  if thing[:foo]
    update_with_bar(thing[:foo])
    if thing[:foo][:bar]
      partial_compute(thing)
    else
      re_compute(thing)
    end
  end
end

# easy
def compute_thing(thing)
  return unless thing[:foo]
  update_with_bar(thing[:foo])
  return re_compute(thing) unless thing[:foo][:bar]
  partial_compute(thing)
end

10. Add underscores to large numeric literals

# hard
num = 1000000

# easy
num = 1_000_000

The above examples are adapted from the Ruby Style Guide and are by no means an exhaustive list. Check out the main style guide for your programming language to get a more specific and complete list.

All is well that ends ...

The benefits of readability cannot be overstated. It could result in huge cost and time savings and play a key role in the outcome of a project.

The good news is there are several tools that help with writing easy-to-read code. Code editors now have in-built support for common guidelines, and linters can be used to enforce a particular style guide. So, the next time your annoying linter flags up a violation, take a deep breath, smile, and remember the bigger picture... then fix it!

race finish line

Thanks for reading! Let others know you found it useful by sharing and dropping lots of emojis. All comments are welcomed.

Until next time... keep calm and carry on writing quality code!