Three Important Factors to Consider When You Write New Code

You may have heard a term called Object Calisthenics, it is not something you would need to be scared of but it simply refers to a set of coding standards and guidelines that we need to follow when producing better software applications. In this article, we will discuss three main factors that we need to consider when we write a piece of code. By following these rules, your code would be more readable, maintainable and also impact the quality of the software you produce.

1. No Abbreviations

Do not abbreviate your code. As a beginner developer, you may break this one at some point or another. Here is what I mean. Have you ever done something like this; imagine you had a translator class and you wrote your code like this:

<?php

class Trnsltr
{

}

Never under any circumstances create a class name or any variable name like this. Why do you think a novice developer would do this? As a new developer, we feel the need to abbreviate everything, including class names, variables, etc. There is absolutely no benefit if you use abbreviations like this, so don’t do it.

The correct way is to write it out manually. Once it goes into your muscle memory, the problem will be fixed.

<?php

class Translator
{

}

Let’s take another example of writing foreach. This is also a common mistake that we all made when we were first getting into development.

<?php

foreach($people as $x)
{
  .
  .
  .
  $x->name

}

Let’s say you wrote the above code six months ago and the code in between foreach has grown by a few more lines. It may take you more than a couple of seconds to remember what $x is equal to. So why bother naming something as $x when you can use a real name like $person since new IDE’s can easily auto complete these for us? Once again, there is zero reason to abbreviate.

What about other offenders? We pointed out class names and variables but this applies to method names as well. Let’s say you have UserRepository class, on which we have a method named fetch to fetch user information by their billing ID. (It is not by their user ID) Imagine coming back to this project after a year or two, you will get really confused by this method because it looks like a fetch method to get user information by ID but the method it self needs billing ID.

<?php

class UserRepository
{
  public function fetch($billingId)
  {
    ...
  }
}

The problem with the above method is that the fetch function is sort of an abbreviation. What we are really doing here is to fetch user information by billing ID so we could write more meaningful code such as the one given below:

<?php

class UserRepository
{
  public function fetchByBillingId($id)
  {
    ...
  }
}

2. Do not use Else

Is it possible that the else condition within a method of yours is either unnecessary or redundant? Yes it could be!

Consider the following code snippet. Imagine that your company has some rules that says we only work on Fridays and we believe in this so much that we do not allow you to post to the company blog if it is Friday.

<?php

public function store()
{
  $input = Request::all();
  $validation = Validator::make($input, ['name' => 'required']);

  if (date('l') !== 'Friday')
  {
    if ($validation->passes())
    {
      Post::create($input);

      return Redirect::home();
    }
    else
    {
      return Redirect::back()->withInput()->withErrors($validation);
    }
  }
  else
  {
    throw new AcmeException('We do not work on Fridays!');
  }
}

Let’s see how we might improve the above code. Notice that we already have a simple method with a few branches (if/else) that we need to understand each and every time we read the code.

Consider the if ($validation->passes()) condition. If it gets true it will return immediately. During situations when you return within one conditional, that usually means that else will get redundant. That means we could remove the else condition and modify the code as follows:

<?php

public function store()
{
  $input = Request::all();
  $validation = Validator::make($input, ['name' => 'required']);

  if (date('l') !== 'Friday')
  {
    if ($validation->passes())
    {
      Post::create($input);

      return Redirect::home();
    }

    return Redirect::back()->withInput()->withErrors($validation);
  }
  else
  {
    throw new AcmeException('We do not work on Fridays!');
  }
}

For the above scenario you could also think of something like defensive programming approach, so in the above example we would have to check if the validation has failed first. That way, our happy path is always at the bottom of the code. Now, we could modify the code as follows:

<?php

public function store()
{
  $input = Request::all();
  $validation = Validator::make($input, ['name' => 'required']);

  if (date('l') !== 'Friday')
  {
    if ($validation->failed())
    {
      return Redirect::back()->withInput()->withErrors($validation);
    }

    Post::create($input);

    return Redirect::home();
  }
  else
  {
    throw new AcmeException('We do not work on Fridays!');
  }
}

We could use our defensive approach for the top level if statement as well. So now, we can even modify our code as follows:

<?php

public function store()
{
  $input = Request::all();
  $validation = Validator::make($input, ['name' => 'required']);

  if (date('l') === 'Friday')
  {
    throw new AcmeException('We do not work on Fridays!');
  }

  if ($validation->failed())
  {
    return Redirect::back()->withInput()->withErrors($validation);
  }

  Post::create($input);

  return Redirect::home();
}

Finally, after a couple of tweaks here we have successfully removed two else conditionals and have decreased the indentation: which, almost always, is an indication that you are on the right track.

3. One level of indentation

This guideline is very important. Once you really force yourself to follow it, you would definitely improve the quality of the code you are writing.

One level of indentation? You may feel that this is impossible to accomplish but maybe there could be lots of ways we could adopt this rule into our code. Let’s write an example for that.

Imagine that we have a kind of BankAccount collection class and Account class. Assume that we would need to give ourselves a way to filter down all of our active Accounts according to some specific type (saving/checking).

<?php

class BankAccount
{

  protected $accounts;

  function __construct($accounts)
  {
    $this->accounts = $accounts;
  }

  public function filterBy($accountType)
  {
    $filtered = [];

    foreach($this->accounts as $account)
    {
        if ($account->type() == $accountType)
        {
          if ($account->isActive())
          {
            $filtered[] = $account;
          }
        }
    }
    return $filtered;
  }
}

class Account
{

  protected $type;

  private function __construct($type)
  {
    return $this->type = $type;
  }

  /**
   * In order to open an account
   * you must use this method.
   */
  public static function open($type)
  {
    return new static($type);
  }

  public function type()
  {
    return $this->type;
  }

  public function isActive()
  {
    // for this example all account are `active`
    return true;
  }

}

$accounts = [
  Account::open('checking'),
  Account::open('saving'),
  Account::open('saving'),
  Account::open('checking')
];

$accounts = new BankAccount($accounts);

$saving = $accounts->filterBy('checking');

var_dump($saving);

Output:
array(2) {
  [0]=>
  object(Account)#1 (1) {
    ["type":protected]=>
    string(8) "checking"
  }
  [1]=>
  object(Account)#4 (1) {
    ["type":protected]=>
    string(8) "checking"
  }
}

Using the above code and looking at the indentation of the filterBy method, you would see that we already have 3 level of indentation. Let’s try to improve filterBy method to follow our One Level Of indentation rule. The first thing to do is to check whether you have nested foreach statements or conditional statements. If so, we could start work from inside out. (Start with the deepest indentation and slowly work your way out)

If you look closely at our 2nd and 3rd level of indentation, it checks if the account type is the one we requested and also if the account is active. If both these “if” conditions are true, the filtered array gets updated. So, that means we could probably combine both “if” conditions together.

public function filterBy($accountType)
{
  $filtered = [];

  // 0 indentation
  foreach($this->accounts as $account)
  {
      // 1 indentation
      if ($account->type() == $accountType && $account->isActive())
      {
        // 2 indentation
        $filtered[] = $account;
      }
  }
  return $filtered;
}

We have now removed one level of indentation. Next we could optimize the “if” condition a bit more. The reason is when you come back to this code snippet 6 months from now, you would have to parse what this does to your head. You would need to go over it and say “alright, if the account type is what we passed in and also the account is active then I could move on.” There is simply too much processing for us to do this way.

So one of the practices we could do, is to extract the condition into a method; now, the key here is to write a meaningful method as best as you can. So in this case, we could re-imagine (not sure what is said here) Is of the type so we could name the method like isOfType. By extracting the condition into a method, it could be re-used in the class if necessary.

public function filterBy($accountType)
{
  $filtered = [];

  // 0 indentation
  foreach($this->accounts as $account)
  {
      // 1 indentation
      if ($this->isOfType($accountType, $account))
      {
        // 2 indentation
        $filtered[] = $account;
      }
  }
  return $filtered;
}

The above refactoring does nothing for our indentation, but it does a lot for code readability which is equally as important. We still have 2 levels of indentation; what else can we do for that? Well, if you see the foreach we are just filtering through our accounts and returning a subset of them (only one has the type we specified.) Maybe foreach is not the right choice. Maybe we should use a function that was specifically designed for filtering such as the php array_filter function. So let’s make sure we use it.

public function filterBy($accountType)
{
  return array_filter($this->accounts, function ($account) use ($accountType) {
    return $this->isOfType($accountType, $account);
  });
}

Here is the complete code:

<?php

class BackAccount
{

  protected $accounts;

  function __construct($accounts)
  {
    $this->accounts = $accounts;
  }

  public function filterBy($accountType)
  {
    return array_filter($this->accounts, function ($account) use ($accountType) {
      return $this->isOfType($accountType, $account);
    });
  }

  private function isOfType($accountType, $account)
  {
    return ($account->type() == $accountType && $account->isActive());
  }

}

class Account
{

  protected $type;

  private function __construct($type)
  {
    return $this->type = $type;
  }

  /**
   * In order to open an account
   * you must use this method.
   */
  public static function open($type)
  {
    return new static($type);
  }

  public function type()
  {
    return $this->type;
  }

  public function isActive()
  {
    // for this example all account are `active`
    return true;
  }

}

$accounts = [
  Account::open('checking'),
  Account::open('saving'),
  Account::open('saving'),
  Account::open('checking')
];

$accounts = new BackAccount($accounts);

$saving = $accounts->filterBy('saving');

var_dump($saving);

If you check our code now, we have achieved the exact same end result but we have exactly one level of indentations. Originally we had 3 levels of indentations, further, imagine if within those 3 levels of indentations we had some “else” statements where we have some situations where we were branching our logic. All of that stuff, specially in 6 months from now, begins to add-up resulting in your code becoming lengthier. As a result, that would become difficult to take in.

So now, by following this approach where we are super sensitive to this idea of deep indentation levels, we actually improved the design and the readability of our class in the process. Remember it is not just about achieving some arbitrary rule, it is about finding little techniques that help you to improve your software.

Happy coding!

Gayan Virajith Tech Lead - PHP September 08, 2017