Developer Standards and Coding Ethos

10 min read

Developer Standards and Coding Ethos

What are Developer Standards?

Developer standards are a collection of rules, guidelines, and best practices. They are designed to unify development teams or projects.

Why should you have them?

Regardless of how people learn to code, they will all have their own idiosyncrasies, and writing code without agreed standards can easily become messy, unmaintainable, and bloated on larger codebases. When working with others, and/or on multiple products, the need to limit barriers to entry is high, to allow for easier onboarding and faster context switching. One way that this barrier can be removed, or at least drastically reduced, is with the use of Developer Standards. This gives every developer, regardless of longevity/seniority, a base understanding of how things are done, and why they are done.

Standards are designed to help with:

  • Ensuring you write cleaner, more readable, and more efficient code
  • Avoiding common coding errors
  • Standardising the formatting of code
  • Onboarding new team members to projects
  • Reducing code complexity
  • Reducing development cost
  • Reducing reliance / dependency on individuals

Don’t forget, PSR and framing conventions should take precedence.

Coding Guidelines

Avoid Obvious Comments

Comments in code are there to make the code easier to understand, or to give additional context/information that is not inherently obvious. Well named variables and functions can provide self documenting comments in your code instead.

Which example below is easiest to read/understand?

Example 1: Overly Verbose

// Get the number of seconds in a day then display it

// Set variable for the number of seconds in a minute
$seconds = 60;

// Set the variable for the number of minutes in an hour
$minutes = 60;

// Set the variable for the number of hours in a day
$hours = 24;

// Multiply the number of seconds in a minute by the number of minutes in an hour,
// then by the number of hours in a day to get the total number of seconds in a day
$result = $seconds * $minutes * $hours;

// Display the number of seconds in a day
echo $result;

Example 2: Concise

// Get the number of seconds in a day then display it
$secondsInMinute = 60;
$minutesInHour = 60;
$hoursInDay = 24;

$secondsInDay = $secondsInMinute * $minutesInHour * $hoursInDay;

echo $secondsInDay;

Example 3: Self-Documenting

$secondsInDay = getSecondsInDay();
echo 'There are ' . $secondsInDay . ' seconds in a day.';

function getSecondsInDay(): int
{
    $secondsInMinute = 60;
    $minutesInHour = 60;
    $hoursInDay = 24;

    return $secondsInMinute * $minutesInHour * $hoursInDay;
}

Clarity is better than brevity

“Programs must be written for people to read, and only incidentally for machines to execute.” — Harold Abelson

Ensure that code is written in as easy a way to understand as possible. Avoid abbreviations where possible, ensure variable names are verbose enough to understand what they contain.

Readable code is almost always more beneficial than writing a few less characters.

DRY (Don’t Repeat Yourself)

“Every piece of knowledge must have a single, unambiguous, authoritative representation within a system”

DRY is a principle that is aimed at reducing repetition of software patterns, replacing it with abstractions or using data normalisation to avoid redundancy.

However, there needs to be a fine balance when writing code - between ensuring that as little of the codebase is duplicated as possible, and ensuring that the code is not abstracted for the sake of abstracting, and being hard to decipher.

Emphasis should be placed on creating reusable functions, interfaces, or classes etc. The focus on this should be on reducing code that is fundamentally the same, and not code that is coincidentally the same, but this needs to be done with care and thought. Basely abstracting/refactoring code may actually have unintended consequences.

The main advantages of a codebase that follows a well structured DRY approach are:

  • Maintainability - it is easier to update a single function, than find every instance and update them all
  • Readability - generally extra effort is put into writing code to ensure it follows a DRY principle, this then becomes easier to read by default.
  • Testing - less tests will need to be written if we have a single function controlling logic.

NOTE

Not all code must be DRY. Sometimes it is better to have two pieces of code that are very similar but have subtle differences because it will be easier to maintain going forward, or merging them could create complicated logic / looping / nesting.

Avoid Deep Nesting Structure

Excessive nesting structures can make code harder to decipher and maintain.

Instead of deep nesting, think about returning early on negative/guard checks, ensuring that if/if statements are refactored, utilise if/elseif where they are needed. If you have tried to refactor this to use some of these ideas, then consider moving code to its own function with an expressive name to make code easier to read.

Nested structure

The actual creation logic is nested 3 levels deep. Whilst this example is simple, it will lead to heavier cognitive load on the developer, having to remember how many states that are being checked. It also makes testing harder, as you have to ensure you’re hitting every nested level correctly.

// Deep nested code can be harder on the eyes to follow
function registerUser($data) {
    if (!empty($data['email'])) {
        if (filter_var($data['email'], FILTER_VALIDATE_EMAIL)) {
            if (!User::exists($data['email'])) {
                // The actual logic is buried 3 levels deep
                return User::create($data);
            } else {
                return 'User exists';
            }
        } else {
            return 'Invalid email';
        }
    } else {
        return 'Email required';
    }
}

Early returns, and clean logic

Conversely, with having early returns, it is much easier for you to scan the code and see each individual reason for the code to return failure messages. There is less cognitive load, and it’s much easier for you to make tests.

// Simple early returns on failures, no longer nested, easier to follow
function registerUser($data) {
    if (empty($data['email'])) {
        return 'Email required';
    }

    if (!filter_var($data['email'], FILTER_VALIDATE_EMAIL)) {
        return 'Invalid email';
    }

    if (User::exists($data['email'])) {
        return 'User exists';
    }

    // Happy path is clean and un-nested
    return User::create($data);
}

Maintain Short Length Line

Narrow and tall columns are easier to understand and comfortable for your eyes. Thus, it is advised to utilise short line length. The perfect length may be about 80 characters, check the coding standards of your specific language for any additional information here.

KISS (Keep it Simple, Stupid / Keep It Super Simple / Keep It Simple and Straightforward)

Simplicity is important.

You should ensure that the code you write is easy to understand. If you put a project down for 3 months and come back to it, you shouldn’t need to spend 2 hours trying to decipher the inner workings. If you are, then the code is most likely over engineered and will most likely lead to the introduction of bugs.

Whilst complexity on some sites will be required, it should be broken down as much as possible to try and make the problem as simple as possible.

If code is no longer required, delete it, do not comment it out. All code should be stored in version control, if you really need it, you have a git history!

Don’t reinvent the wheel

This is more about finding the balance between writing new code, utilising framework specific functionality, and incorporating third party code into systems.

There are countless open source projects that have been thoroughly tested, are well maintained, and have an active follower base that will ensure that most errors and edge cases are already picked up and have had pull requests applied against them.

Can you save time by utilising one of these libraries, instead of writing your own? For example, every language will allow a developer to load a CSV file in and convert to an array. All developers should be able to do this, but by utilising a library that has been finessed to work very well, and incorporating additional features that might come in handy is better use of time, than to write something that will only do a fraction of the job.

Naming Conventions

Follow standards that exist within the appropriate language / framework, however you should try to ensure that your functions and variables are as descriptive as possible.

This will help by:

  • Keeping code understandable
  • Saving debugging time
  • Reduces the time and effort needed to understand the code
  • Speeds up code reviews as descriptive names will inherently explain the code better
  • Avoids ambiguity
  • Saves time in searching for variables / functions

Without comments, what is easier to understand?

// Without appropriately named variables
$x = ($a * $b * $c) / 100;

// With appropriately named variables
$interest = ($principal * $interestRate * $timeInYears) / 100;

Indentation

PSR has this covered!

Brackets

Always try to open and close all control structures with brackets (if applicable). Even if you can use a control structure without it and have a single line of code execute, it can be confusing, especially if indentation is not correctly applied.

if ($a === true)
    doSomething();
    someOtherFunction();

/* --------------------- */

if ($a === true) {
    doSomething();
}

someOtherFunction();

Linting

Always be linting. Why should you use linting?

  • Consistent codebase improves maintainability
  • Passive learning from linting
  • Remove unnecessary decision-making and focus on solving the problem
  • Avoid easy to make errors
  • You won’t come back to it later
  • Easy to set up

Testing Your Work

Why

No one wants to write code with bugs, but it happens.

Regardless of if you are pro TDD or not, testing is an essential part of the development lifecycle, helping to ensure you deliver high quality code. Having tests will deliver the following benefits:

  • Ensures you deliver high quality work
  • Validate code against your requirements
  • Increase understanding of codebase
  • Passive learning when fixing bugs (what not to do in the future)
  • Confidence in refactoring code
  • Improve code quality
  • Faster development cycles

Don’t forget! You shouldn’t only test the happy paths in your code, think outside the box and how your code is going to handle and deal with incorrect use.

Types of testing

I’m not saying that every single line of code needs to be tested, sometimes that’s not feasible. But you should always make sure that your code has the critical paths and core components tested.

If you’re refactoring code without tests, you are much more likely to introduce bugs than if you have a full test suite covering the functionality you’re refactoring.

Unit Testing

A Unit Test is very low level and directly interrogates the code. It should test a specific standalone piece of code (e.g. a function, or a class), separately from the rest of the application, and will compare actual results against expected results. It should not connect to queues, or databases, or anything like that.

These should be the easiest/quickest tests to write, they are also “cheap” as they shouldn’t require the entire application to be up and running. These tests will also “self document” as it will show other developers how that piece of code is expected to work from a given input/output.

Feature/Functional (Integration) Testing

These are quite high level, and focus on ensuring that the application meets the requirements.

These tests will be more complicated than Unit Tests, and are more “expensive” as they will generally require the application to be running, and a test database seeded with enough data to fulfil the test. Functional Tests are not concerned with the actual code and how it works, but rather the user journey and ensuring that the system behaves in a specific way and returns the correct responses. Ensuring that the outcome is what is expected from the inputs given.

Manual Testing

Whilst Unit and Feature Tests are critical, it’s always useful to just run through your code as an end user would and make sure it’s working. Bugs might not always be apparent when you’re deep in the code!