code

How to write code that speaks for itself

Yashu Mittal

The very real danger with commenting code is that it becomes outdated while the codebase rapidly evolves. And out of date comments are far worse than no comments. So what’s the alternative?

At CodeCarrot we aim for fast iterations as part of that goal we’ve come to the opinion that comments in code are not worthwhile. That’s not to say we never comment code, but it’s a guideline to write code that is easy to read.

The very real danger with commenting code is that it becomes outdated while the codebase rapidly evolves. And out of date comments are far worse than no comments. So what’s the alternative?

Self documenting code is code that speaks for itself. The code becomes an understandable piece of text that tells stories about business objects that interact with each other in various contexts.

We’ve gathered our team’s thoughts about how to make readable code:

Write short methods

If you find methods starting to get longer than 5 lines it’s doing too much. Try to shorten it down.

def process_payment(payment)
  charge_service = ChargeCustomerService.new(payment).call
  if charge_service.success?
    PaymentsMailer.notify_order_complete(payment.user)
  else
    redirect_to new_payment_url, alert: charge_service.error
  end
end

#  becomes:
def process_payment(payment)
  charge_service = ChargeCustomerService.new(payment).call
  charge_service.success? ? success(payment) : error(charge_service.error)
end

def success(payment)
  PaymentsMailer.notify_order_complete(payment.user)
end

def error(error)
  redirect_to new_payment_url, alert: error
end

Carefully choosing method names

A method like #inspect is short and simple and perfectly describes what it does. Ruby is full of these kinds of methods: Time.now, each, upcase, is_a?, instance_of?

We’ve already established (step 1) that the method will do one thing, so name it by what it does. Short and sweet is the goal… Examples: authorize_user, save_in_background. If a method takes arguments it is possible to take this into account: authorize(user), json_for(product).

In ruby adding a ! to a method tells you the method will modify the object it is called on.

title = 'Ruby Ruby Ruby Ruby!'
title.downcase!
title # => 'ruby ruby ruby ruby!'

Add a ? to any method that returns true or false.

nil.present? # => false
nil.nil?     # => true

Variable naming

Variables are a way to cheat if you weren’t able to find a good name for the method. @user = GoogleAuthService.call(security_token) Finding the perfect name for a variable is difficult. But it will pay off the next time someone looks into the code.

Use common design idioms

Use Ruby’s opinions on method naming

Reuse method names like call and to_s that developers already understand from ruby. Ruby uses the call method for an executable block of code. That’s why it’s good practice to use the call method in a service. That way if someone comes along and sees a call method, they can already guess how it works.

Service Objects

Services are a great place to group logical pieces of functionality. In fact, inspecting the services folder of an application should tell the programmer what the application really does, which is not always obvious when looking at controllers or models.

Decorators

Decorators let you create methods on objects that return data in only the contexts they are needed. The most common place to use a decorator is in the view.

class ProductDecorator < Draper::Decorator
  def short_title
    object.title.truncate_words(5)
  end
end

Readability > Optimization

Optimization is great. In databases and other places where it actually makes a difference to response times. Premature optimization: you don’t know if it will actually make a difference. Skipping a more verbal series of steps for a few shortcuts might shave off some milliseconds, but it adds minutes to the next time you try debug what you wrote.

Writing specs

Testing code properly will be a guide to future developers when they approach your codebase. They’ll get an overview of the flow of the application. The same ideas with improving code readability apply to writing specs!

Focus on contexts in tests. They are useful for separating specific behaviors and conditions in the program’s flow.

describe "Admin dashboard" do
  let(:admin) { create :user, :admin }

  context "with a new notification" do
    let(:notification) { create :notification }

    it "displays the notice" do
      log_in_admin
      visit admin_root_path
      expect(page).to have_content 'New notification'
      expect(page).to have_content notification.title
    end
  end
end

The naming in each block is important. When the test fails it will read in a way that makes sense: Failed: Admin dashboard with a new notification displays the notice.

Use language style guides

At CodeCarrot we use automated checking of our style with Thoughtbot’s hound which uses rubocop. If that wasn’t enough our code reviews not only check code quality but also code readability.

There are style guides for:

Response to “How to write code that speaks for itself”

Stay current

Sign up for our newsletter, and we'll send you news and tutorials on business, growth, web design, coding and more!