Coding Standards – a work in progress

“We endeavor to write well-functioning and elegant code which is both readable and maintainable in the long term.”


1) Style

1.1 – Use PSR-12 for all code styles in PHP, and set your IDE to test and format automatically following the PSR-12 standards. If we are all on the same page with the style standard, our code will look nice across the board. More importantly, a consistent style will make reviewing and maintaining our code much faster.


2) Syntax

Write clear code, with the fewest keystrokes possible. If you have an opportunity to simplify your code, then please do!

Always use <?= instead of <?php echo. The shorthand is just as readable, takes slightly less space, and is much quicker to type.

Use || and && and not or and in if-statements. This makes the operators stand out and easier to read.

Don’t test if ($thing == true). Instead, if ($thing) works just fine. It’s faster to type, and there’s no need to repeat yourself. Note: the same rule applies for if (!$thing).

Use the ternary operators whenever possible. When an if-then can be simplified, please do it. If the code winds up looking more confusing with a ternary, then it’s okay to use if-then.

Use the coalesce ?? operator rather than is_null when possible. For example: if (!is_null($var)) $returnVal == $var else $returnVal == ‘default’; is not as good as $returnVal == $val ?? ‘default’; The latter kills two birds with one stone.

 


3) Git

Always Rebase feature branches. Merges can cause problems. Rebase is the best way to keep your history intact. Additionally, seeing a ton of merge commits and arrows is very messy!

Keep feature branches short-lived. Old branches don’t age well. The longer they live, the more trouble they give you when rebasing. You should be able to break a large feature into smaller, incremental branches to keep your work up-to-date.

If a branch has already been merged and you have bugs to fix- make a new branch. Doing this will keep things organized and remove any ambiguity as to whether a branch on the remote should be deleted or not. i.e. If the branch was merged, it can be deleted.

Always delete feature branches from the remote after merge. Keeps the repo nice and tidy.

Always delete release branches from the remote after merge and release. Keeps the repo nice and tidy.

Never commit commented-out code. You will never need it again, and odds are no one else will use it either. The exception is when you are temporarily disabling a feature, with the intention of bringing it back later, and there isn’t a better option available. (However, you should consider adding a .env variable that enables/disables a feature. Thus, changing its value will not require any further changes to the code.)

 


4) Best Practices and Design Patterns

Use the Single Responsibility Principle for everything. Literally, everything. Write one thing to do one thing well. Then, you can re-use it and it makes our code tons more maintainable.

Always fast-exit your methods when possible. Why bother wrapping an entire code block in an if-else, when you could just quickly exit and not wrap the rest of the code at all?

Example:

// BAD
public function getUserList()
{
  if (Auth::user->hasRole('can_list_user')) {
    $list = User::getList();
    return $list;
  } else {
    return false;
  }
}

// GOOD
public function getUserList()
{
  if (!Auth::user->hasRole('can_list_user')) {
    return false;
  }
  
  return User::getList();
}

Use constants, enum types, configs, and other globally-defined tools and never hard-code values into properties and methods. If we hard-code values, then what happens if it changes someday? Using some sort of globally-defined value makes the code more maintainable, and improves the reusability of the code.

Always test for null or empty vars. By doing this as a rule, you will avoid a lot of unanticipated bugs in the future.

Method names should never need an explanation. If you find yourself needing to write comments to describe what a method does, then your method needs a better name. Consider a future developer who needs to maintain the code 3 years in the future, and write the name for them.

Keep your Controllers lightweight and focused on controlling the flow of ins and outs. A controller should do one thing: take inputs and use them to create outputs. Therefore, all business logic should be in a Service class.

Keep your Service classes generic. Service classes should only take in the information needed to respond with some output. They should not transform the data into any kind of response or view-related object type; that’s what a Controller should do.

Keep your Models clean and lightweight. Models should provide a clean Data Access Object (DAO). This includes tables, columns, and relationships, as well as any helper methods such as Accessors or Mutators. Otherwise, any other business logic should be contained in a Service class.

Service, Action, and Model classes should only ever throw errors. In other words, always leave your error-handling to the view logic in the Controller.