~ RICH CODES

How Spaces Can Improve Your Code

August 31, 2021 | 4 minute read | ✍🏼 Fix typo

Writing code is similar to writing an essay: it consists of many small parts that interact. One big giant blob of words is hard — and tedious — to read. To address this, we use paragraphs. The same technique can benefit our code. Here’s how.

Logical blocks

A paragraph is a self-contained unit of discourse in writing dealing with a particular point or idea. — Wikipedia

The critical thing in this definition is “dealing with a particular point or idea”. Structuring our code with paragraphs (i.e., logical blocks) means we group related things and visually separate them from the others. A simple new line makes a huge difference in readability. Let’s look at some examples to clarify this idea.

Separate test phases

Consider this function: it takes two numbers and returns their sum. Pretty simple.

fn sum(a: i64, b: i64) -> i64 {
  a + b
}

A possible unit test for this function could look like this:

#[test]
fn it_sums_two_numbers() {
  let a = 1;
  let b = 2;
  let result = sum(a, b);
  assert_eq!(3, result);
}

While this test follows the four-phase pattern, separating each test phase makes them more clear:

#[test]
fn it_sums_two_numbers() {
  let a = 1;
  let b = 2;

  let result = sum(a, b);

  assert_eq!(3, result);
}

Programmers often use structures like modules and classes to tie code together. It is a great technique, but can lead to clutter if we’re not careful.

Check out this Rails model. Even though it’s only a few lines long, it’s a bit hard to parse this class.

class User < ApplicationRecord
  include Clearance::User
  has_many :books
  belongs_to :organization
  validates :name, presence: true
  validates :email, presence: true, uniqueness: true
end

See how different it looks when we separate its logical blocks:

class User < ApplicationRecord
  include Clearance::User

  has_many :books
  belongs_to :organization

  validates :name, presence: true
  validates :email, presence: true, uniqueness: true
end

Here, we grouped each section by its “theme” (mixins, associations, validations, query methods, etc.). The spacing helps us see the logical separation between each section, which speeds us up when searching where to add and change code.

Separate calculations from the return value

Another great opportunity to use code blocks is in function bodies. We often do some preparation work before return the result, so it’s good to split those two parts up. This technique is especially beneficial when the language allows omitting the return keyword.

FYI: I’m omitting some details in this Rust implementation to keep it simpler.

enum FileFormat {
  HTML,
  Latex,
  Markdown,
  // ...
}

fn render_markdown_to(markdown_content: &str, format_to_convert_to: FileFormat) -> String {
  let renderer = match format_to_convert_to {
    FileFormat::HTML => HTMLRenderer::new(),
    FileFormat::Latex => LatexRenderer::new(),
    other => panic!("Cannot convert markdown to {}", other),
  };
  renderer.render(markdown_content)
}

For those unfamiliar with Rust, omitting the semicolon in the last line indicates the function’s return value. It’s a subtle detail that is easy to miss. Adding a new line here makes it more evident where the calculations end and what is the actual return value.

fn render_markdown_to(markdown_content: &str, format_to_convert_to: FileFormat) -> String {
  let renderer: Renderer = match format_to_convert_to {
    FileFormat::HTML => HTMLRenderer::new(),
    FileFormat::Latex => LatexRenderer::new(),
    other => panic!("Cannot convert markdown to {}", other),
  };

  renderer.render(markdown_content)
}

Extracting behavior

Using logical blocks also helps us to refactor. It’s particularly useful to extract functionality: grouped lines probably will move together. Looking at the last example, we could extract the match expression to its own function:

fn render_markdown_to(markdown_content: &str, format_to_convert_to: FileFormat) -> String {
  let renderer = renderer_for_format(format_to_convert_to);

  renderer.render(markdown_content)
}

fn renderer_for_format(format: FileFormat) -> Box<Renderer> {
  match format {
    FileFormat::HTML => Box::new(HTMLRenderer::new()),
    FileFormat::Latex => Box::new(LatexRenderer::new()),
    other => panic!("Cannot convert markdown to {}", other),
  }
}

Simple but effective

Separating logical blocks is a technique that is really simple but highly effective in making code more maintainable. Remember, we read code more than we write, so optimize your code for reading. A little effort today can save your team hours in the future.

Categories

quick-tip refactoring