There's more to writing code than creating apps that fulfill requirements. Code should be written in such a way that it's maintainable, robust, verifiable, and readable. This is why developers need to constantly find and learn new ways to write better code.
The goal of this blog post is to list a few programming best practices to improve how you write code. While this list is by no means exhaustive - entire books have been written on the topic - I focused on the programming practices that have had the biggest impact on the code I write.
There are many reasons why you would want to write a test before you write the code: verifiability, preventing regression, better documentation, etc... But I particularly like TDD because it introduces a user for the code that will be written.
Let me explain. A test can be thought of as a user of the eventual code. Because of that, a test-first approach enforces the good design of code. TDD forces the developer to write code that is easy to test, i.e. well-designed code.
However, TDD tends to rely too much on a developer's specific implementation, which is why it's often used together with Behavior-Driven Development (BDD). BDD tests the actual behavior of the code. This is particularly useful when refactoring, because you can still tell if the new code correctly implements the required behavior of the old code without having to update your behavior tests.
A pure function is a method that does not depend on an external mutable state. It's a function that always returns the same output when given the same input. Pure functions have a variety of benefits: no side-effects, easier testing, easier debugging, etc...
Consider the following example:
const numbers = [5, 3, 1]
const sortedNumbers = numbers.sort()
console.log(numbers) // [1, 3, 5]
console.log(sortedNumbers) // [1, 3, 5]
console.log(numbers === sortedNumbers) // true
The Array
sort method is an impure function, because it mutates the original array on which it's executed. This could lead to confusing bugs, as the app wouldn't necessarily want to have the array of numbers
in that particular order.
A pure implementation (in ES6
) would look like this:
function sortArray(arr, compareFunction = null) {
return [...arr].sort(compareFunction)
}
const numbers = [5, 3, 1]
const sortedNumbers = sortArray(numbers, (a, b) => a - b)
console.log(numbers) // [5, 3, 1]
console.log(sortedNumbers) // [1, 3, 5]
console.log(numbers === sortedNumbers) // false
This example doesn't mutate the numbers
array anymore. It also allows you to pass an optional sort method that specifies how to sort the array, which makes the sortArray method more reusable.
You might have noticed that this ties into functional programming. Functional programming is a declarative software programming paradigm that avoids changing state and mutable data. As such, functional programming enforces pure functions (as well as many other things).
The idea of a Single Source of Truth (SSOT) is that you store application data in a single place. Links to specific pieces of information are done through reference. This approach helps avoid data sync issues and it creates a clearer separation between data and its representation.
SSOT isn't exclusive to software development. It's used in marketing, product development, sales, and many other parts of an organization.
Double negatives are complicated to understand. Consider this example:
You can't see no one in this crowd.
It's a confusing and misleading sentence. Technically, it means that you can see everyone in the crowd, although the speaker probably means that he can't see anyone in the crowd. Avoid such double negatives in your code by writing boolean
variables positively.
Here's a JavaScript example:
const inactive = false
if (!inactive) {
console.log('is active')
}
Let's improve this code by applying what we learned above. First, add the word is
to the name of the variable (which always implies true/false
). Then rename it to active
, so it's written positively:
const isActive = true
if (isActive) {
console.log('is active')
}
This modified version is much easier to read and understand. As such, always write positive boolean
variables and include is
to imply true/false
. It's not that much of an effort, but it will drastically improve the readability of your code.
Naming things is one of those problems that have haunted software developers since time eternal (well, maybe more since the nineties). But there are a few tricks that developers can use to alleviate the naming problem. One that worked well for me is the verb-noun naming convention.
When you name a method, describe the action it will perform with a verb, such as get
, set
, transform
, or render
. Then include a description of the value that the method will return or update, such as getInvestmentTotal(...args)
, renderHeader(...args)
, or saveUserAvatar(...args)
.
This verb-noun naming convention will help other developers better understand the code you've written.
In this blog post, I listed a few programming best practices that have helped improve the quality of the code I write. I hope they will serve you as much as they have served me.
Want more? Read this clean code principles blog post for other best practices.