Fundamental coding principles

Experience in coding is not necessarily about the intricacies of a language but is as much about concepts. Below is a list of concepts that should be applied regardless of the language.

DRY coding

Don’t Repeat Yourself is a fundamental principal. If there is a temptation to copy and paste a block of code think about how this block can be encapsulated and abstracted in a function or method.

Return quickly

Returning early will help performance and avoid the need for more conditional statements. Look for ways to return early, perhaps transforming:

function transformX(x, type)
{
  let result = false;
  if (type = 'main') {
    result = x + 10;
  } else if (type = 'secondary') {
    result = x + 20 * 2;
  } else {
    result = 0;
  }
  return result;
} 

into

function transformX(x, type)
{
  if (type = 'main') {
    return x + 10;
  }
  if (type = 'secondary') {
    return x + 20 * 2;
  }
  return 0;
}

Separation of concerns

Each function should have a speific purpose. If there is a complex piece of business logic involving multiple steps it is better to split in these into abstracted, self contained functions, link together through a wrapper for the process.

In additional, separation of concerns should extend to the interaction between functions. Once function should not play with anoth functions variables, each function should accept and return independantly of other processes.

As an example, rather than

function saveApproval(rego, odo, date, amountNet, amountGst)
{
  //find the contract
  let sql = 'select id from contracts where rego = ' + rego;
  const contract = execute(sql);
  //validate the odometer
  sql = 'select max(odometer) from maintenance where contract_id = ' contract;
  const currentOdo = execute(sql);
  let odoValid = false;
  if (currentOdo * 0.8 < odo && currentOdo * 1.2 > odo) {
    odoValid = true
  }
  //validate the GST amount
  let gstCode = 'FRE';
  if (amountGst != 0) {
    gstCode = 'GST';
  }
  //save the approval
  sql = `insert into approvals values ({contract}, {odo}, {odoValid}, {gstCode}, {amoutNet}, {amountGst}, {date})`;
  const recordId = execute(sql);
  return recordId;
} 

separate the concerns, making each function reusable and independent.

function saveApproval(rego, odo, date, amountNet, amountGst)
{
  //find the extra detail we need
  const contract = getIdFromRego(rego);
  const gstCode = getGstCode(amountGst);
  const odoValid = isOdoValid(contact, odo);
  //save the approval
  sql = `insert into approvals values ({contract}, {odo}, {odoValid}, {gstCode}, {amoutNet}, {amountGst}, {date})`;
  const recordId = execute(sql);
  return recordId;
}

function getIdFromRego(rego)
{
  //find the contract
  let sql = 'select id from contracts where rego = ' + rego;
  const contract = execute(sql);
  return contract;
}

function isOdoValid(contact, odo)
{
  //validate the odometer
  sql = 'select max(odometer) from maintenance where contract_id = ' contract;
  const currentOdo = execute(sql);
  if (currentOdo * 0.8 < odo && currentOdo * 1.2 > odo) {
    return true;
  }
  return false;
}

function getGstCode(amountGst)
{
  //validate the GST amount
  if (amountGst != 0) {
    return 'GST';
  }
  return 'FRE';
} 

Note that in implementing the separation of concerns that we have also allowed for DRYer code, with each section being reusable, and we have returned from each function as soon as possible.

Implement forced simplicity

Simple is better than complex as it allows for easy debugging, easier future maintenance and easier handovers to other developers.

Avoid more than 2 nested conditional statements. When the bussiness logic is complicated look for alternatives to deep nesting, making the code easier to read and understand. Some of the above principals will help but may require decomposing the conditionals or rethinking the implementation.

Exception handling

Errors should be thrown as exceptions rather than returned as a result from a function. Throwing an exception will force other code to handle the exception rather than a false value slipping through from one functions to the next. It also reduces the conditional statements required in the code for error checking.

Define, don’t accept, a ‘happy path’

Valid data should not be assumed, invalid data should be part of the testing mechanisms. When coding, think about what will happen if an string is passed instead of an int (in a loosely typed language) and cater for these instances.

Documentation

Each company will have their own documentation standards. In general documentation should include general workflow or process documents that outline the application as a whole and documentation inline with the code.

Within the code, comment the logic blocks, not every line, but not absent altogether.

Testing

Whenever possible, test cases should be defined as part of a user story before the coding commences, not as a reaction to the coding. Each functional piece of code should have a test defined before it is considered complete.

Additional Obejct Oriented principles

Object oriented: Use Object oriented structure such as, model view controller, inherit, abstract classes and interfaces. Should think about reuse when build a new class. Basic OO conception and examples : PHP OOP Intro

Single-responsibility Principle (SRP) states: A class should have one and only one reason to change, meaning that a class should have only one job.

Open-closed Principle (OCP) states: Objects or entities should be open for extension but closed for modification. This means that a class should be extendable without modifying the class itself. Examples SOLID: The First 5 Principles of Object Oriented Design | DigitalOcean

Interface Segregation Principle: It states that a client must not be forced to implement an interface that it doesn’t use. It will make sure that Classes are sharing only the knowledge that is required and not just bloating themselves, just because we committed to code to interface. Examples: SOLID: The First 5 Principles of Object Oriented Design | DigitalOcean