Three techniques to make your code more readable

Published by Aaron Price on March 2, 2016

Code readability is arguably the most important part of application development. If I had to choose between an application that worked perfectly, but the code was impossible to read, or an application that was flooded with bugs, but the code was easily readable, I'd always choose the latter. Readable code is easy to understand and therefore easy to fix.

Also, as time passes, and complexity grows in your application, it is very easy to forget why specific lines of code were written, even if you are the author. One solution is to maximize test coverage, but it isn't always practical to write tests for every single possible use case. Consider filtering a list using multiple fields, for example. Complete coverage would require you to test every possible combination of fields to be filtered. If you could filter the list by 5 fields, then you'd have to write 120 tests just to cover positive and neutral cases.

Code readability is also important when you are in a team environment. The less you have to explain your code to a colleague, the easier it is for everyone.

Let's look at an example of a signup controller for a multi-tenanted application.

In this example, the controller:

  • issues a coupon for users who sign up under any subdomain except "foo"
  • verifies the email addresses of users who sign up under the "bar" subdomain

class UsersController < ApplicationController

  def create
    @user = User.new(params[:user])

    if @user.save

      if (request.subdomain == "foo" && @user.id % 2 == 0) || request.subdomain != "foo"
        coupon = Coupon.new
        coupon.name = "20% off your first purchase"
        coupon.discount_percent = 20.0
        coupon.save

        @user.coupons << coupons
      end

      @user.verification_code = SecureRandom.hex(30)
      @user.save

      if request.subdomain == "bar"
        flash[:success] = "Great! we just need you to verify your email before signing in."
        redirect_to new_session_url
      else
        session[:user_key] = @user.session_key

        flash[:success] = "Welcome [email protected]_name}"
        redirect_to profile_url
      end

    else
      flash[:error] = "Oops! be sure all required fields are filled out."
      render :new
    end

  end
end

With just two requirements, this controller is starting to look messy. So here are some ways we can make it more readable.

1. Extract isolated logic

In the above example, the logic to issue a coupon to the newly registered user doesn't rely on any other code in that method, so refactor it out into a separate method.


if @user.save
  assign_coupon
  ...
end

...

def assign_coupon
  if (request.subdomain == "foo" && @user.id % 2 == 0) || request.subdomain != "foo"
    coupon = Coupon.new
    coupon.name = "20% off your first purchase"
    coupon.discount_percent = 20.0
    coupon.save

    @user.coupons << coupons
  end
end

2. Give simple descriptors to complex conditions

Complex if statements can be very hard to read, but wrapping it readable method name makes it a lot easier to work with.


def assign_coupon
  if should_assign_coupon?
    ...
  end
end

...

def should_assign_coupon?
  if (request.subdomain == "foo" && @user.id % 2 == 0) || request.subdomain != "foo"
end

3. Explain what the code is supposed to accomplish


...
else
  session[:user_key] = @user.session_key
  ...
end
...

This line of code is performing a very important action, but would be very easy to miss while running through the code.

So let's give it some meaning.


...
else
  login
  ...
end
...

def login
  session[:user_key] = user.session_key
end

Conclusion

With these three techniques, you can have much more readable code in no time. Let's have a look at the finished product.


class UsersController < ApplicationController

  def create
    @user = User.new(params[:user])

    if @user.save
      assign_coupon
      verify_or_continue
    else
      flash[:error] = "Oops! be sure all required fields are filled out."
      render :new
    end
  end

  private

  def assign_coupon
    if should_assign_coupon?
      coupon = Coupon.new
      coupon.name = "20% off your first purchase"
      coupon.discount_percent = 20.0
      coupon.save

      @user.coupons << coupons
    end
  end

  def should_assign_coupon?
    user_id_is_even = @user.id % 2 == 0
    (request.subdomain == "foo" && user_id_is_even) || request.subdomain != "foo"
  end

  def verification_required?
    request.subdomain == "bar"
  end

  def login
    session[:user_key] = @user.session_key
  end

  def verify_or_continue
    flash_message = "Welcome [email protected]_name}"
    redirect_url = profile_url

    if verification_required?
      flash_message = "Great! we just need you to verify your email before signing in."
      redirect_url = new_session_url

      @user.verification_code = SecureRandom.hex(30)
      @user.save
    else
      login
    end

    flash[:success] = flash_message
    redirect_to redirect_url
  end
end

Even though it looks like more code, breaking code down into smaller, modular blocks of readable code makes it easy to build upon, and easier to find bugs if they exist.

About me...

My name is Aaron Price. I'm a ruby developer based in Toronto and I'm passionate about simplicity.

Feel free to reach out using the following links: