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 specific 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. One function should not play with another functions variables, each function should accept and return independently of other processes.
As an example, rather than
function saveInvoice(clientId, itemId, date, amountNet, amountGst) { //find the client let sql = 'select id, name, email from clients where id = ' + clientId; const client = execute(sql); //validate the item sql = `select i.id, i.description, count(s.qty) as stock_qty from item i inner join stock s on i.id = s.item_id where i.id = ${itemId}`; const item = execute(sql); let backOrder = false; if (item.stock_qty < 1) { backOrder = true } //validate the GST amount let gstCode = 'FRE'; if (amountGst != 0) { gstCode = 'GST'; } //save the approval sql = `insert into invoices values (${clientId}, ${client.email}, ${backOrder}, ${gstCode}, ${amountNet}, ${amountGst}, ${date})`; const recordId = execute(sql); return recordId; }
separate the concerns, making each function reusable and independent.
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.
function saveApproval(clientId, itemId, date, amountNet, amountGst) { //find the extra detail we need const email = getClientEmail(clientId); const gstCode = getGstCode(amountGst); const backOrder = isBackOrder(itemId); //save the approval sql = `insert into invoices values (${clientId}, ${email}, ${backOrder}, ${gstCode}, ${amountNet}, ${amountGst}, ${date})`; const recordId = execute(sql); return recordId; } function getClientEmail(clientId) { //find the client let sql = 'select id, name, email from clients where id = ' + clientId; const client = execute(sql); return client.email; } function isBackOrder(itemId) { //validate the odometer sql = `select i.id, i.description, count(s.qty) as stockQty from item i inner join stock s on i.id = s.item_id where i.id = ${itemId}`; const item = execute(sql); if (item.stockQty < 1) { backOrder = true } return false; } function getGstCode(amountGst) { //validate the GST amount if (amountGst != 0) { return 'GST'; } return 'FRE'; }
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 business 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 Object 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