Introduction
One of the beautiful things about programming is that there are hundreds of different roads to the same destination. Everyone has their own style and everyone has their own preferences. Whatever your style or preference though, there are some simple techniques you can adopt to make your life, and the life collaborators (including your future self) easier.
Use descriptive names
Using descriptive names is one of the most straight forward changes you can make to your workflow. By using descriptive names for functions and variables, your code can become almost entirely self documenting, although I would still suggest adding comments to complex functionality. We've all been there, you open an old project or a project you've inherited from another developer and it's full of generic names which mean nothing. Take the following example, the code is fetching some items
from which it's creating a new Array, Alt items
, before setting the icon
property to 'star'. In this instance you as the developer, have no idea what those items
that are being fetched and manipulated are:
let items = fetchItems()
let altItems = items.filter(item => item.featured == true)
altItems.forEach(item => {
item.icon = 'star'
})
You can easily make this code snippet more understandable with descriptive names. For arguments sake, let's say your fetch function is returning an array
of articles
. Now see the following example for how you may choose to rewrite your previous code snippet with more descriptive names:
let articles = fetchArticles()
let featuredArticles = articles.filter(article => article.featured == true)
featuredArticles.forEach(featuredArticle => {
featuredArticle.icon = 'star'
})
Extract common functions
It's not uncommon when writing in any programming language to want perform repetitive tasks within functions and loops. Of course you can repeat yourself each time, however your code will soon become hard to maintain. Take the following example, there are 2 loops, one for articles
and one for projects
, for each article
/ project
the code will add a standardised formatted date:
articles.forEach(article => {
let date = new Date(article.createdAt),
day = date.getDate(),
month = date.getMonth()+1,
year = date.getFullYear()
article.formattedDate = `${day}/${month}/{year}`
})
projects.forEach(project => {
let date = new Date(project.createdAt),
day = date.getDate(),
month = date.getMonth()+1,
year = date.getFullYear()
project.formattedDate = `${day}/${month}/{year}`
})
Imagine now you wanted to change the date format, you now have 2 places you need to update your formatting, the articles
loop and the projects
loop. However by extracting the date functionality from each of the loops and creating a new function, you can reuse the same code in each. Then, should you wish to change your date format, you only need to do it in one place, even if you have hundreds of instances. Now see the following example for how you might choose to refactor your previous snippet and extract the date formatting functionality.
const formatDate = (rawDate) => {
let date = new Date(rawDate),
day = date.getDate(),
month = date.getMonth()+1,
year = date.getFullYear()
return `${day}/${month}/{year}`
}
articles.forEach(article => {
article.formattedDate = formatDate(article.createdAt)
})
projects.forEach(project => {
project.formattedDate = formatDate(project.createdAt)
})
In situations like this, you could argue that defining variables inside your extracted function offers very little in the way of extra clarity. In that case use the date values directly:
const formatDate = (rawDate) => {
let date = new Date(rawDate)
return `${date.getDate()}/${date.getMonth()+1}/{date.getFullYear()}`
}
Avoid nested conditional statements and return early
Conditional statements are common place in almost every Web Application, determining whether statement is true or false and returning or setting the relevant data. The problem with conditionals is that they can very quickly become unsightly and unreadable. Take the following example, the getShopTradingStatement
function returns a string based on whether the shop is currently trading and if it is, whether the shop has had its opening day:
const getShopTradingStatement = (shop) => {
if (shop.isTrading) {
if (shop.openFrom < Date.now()) {
return `This shop is currently trading!`
}
else {
return `This shop will open it's doors from ${shop.openFrom}`
}
}
else {
return `This shop is no longer trading.`
}
}
Nesting statements in this way, while perfectly valid, already feels cluttered. By switching up your statements and taking advantage of early returns you can make this very same functionality much tidier. Now see the following example for how you might choose to refactor your previous snippet by returning the output of your primary else
statement early:
const getShopTradingStatement = (shop) => {
if (!shop.isTrading) return `This shop is no longer trading.`
if (shop.openFrom < Date.now()) return `This shop is currently trading!`
return `This shop will open it's doors from ${shop.openFrom}`
}
Use look up tables
Look up tables are a fantastic way of cleaning up messy, repetitive if
statements. Take the following example, you have a userHasAccessPermissionsForTask
function which accepts the users role and the task they'd like to perform. The function checks against 3 possible user roles and associated permissions for each. If the user role has permissions to perform the task, the function will return true
else it will return false
:
const userHasAccessPermissionsForTask = (userRole, task) => {
if (userRole == 'admin' && ['write', 'edit', 'read'].includes(task)) {
return true
}
if (userRole == 'editor' && ['edit', 'read'].includes(task)) {
return true
}
if (userRole == 'subscriber' && ['read'].includes(task)) {
return true
}
return false
}
The previous code snippet is perfectly valid but it's not particularly clear. You're also repeating your includes
function on each if
statement, if in future you wanted to test against that data in a different way you'd need to ensure you did it in 3 places. You could make the argument for extracting that functionality as it's own function but creating a function to return a built in function seems a little odd, right? Luckily with look up tables you can avoid all that. Now see the following example for how you might choose to refactor your previous snippet using a look up table. The look up table is an object that, in this case, uses the user roles as keys and permissions as values. You can now return a single includes
function which references the permissions
object property with the key equal to the given userRole
:
const userHasAccessPermissionsForTask = (userRole, task) => {
const permissions = {
admin: ['write', 'edit', 'read'],
editor: ['edit', 'read'],
subscriber: ['read']
}
return permissions[userRole].includes(task)
}
Start sharing your code
I think sharing your code is possibly one of the most valuable things you can do if you want to continue to grow and learn as a developer. Things change fast in tech and it's very easy to be left behind, particularly when you're busy. Sharing your code, whether via code reviews, pair programming*, even through blogging and social media platforms like Twitter allows people to question your approach. That can seem scary at first, but being questioned on your approach does not mean you're wrong, but it allows you to open your mind to new and/or different ways in which you might approach issues in future. In turn, your answers may help serve those questioning you in their future projects.
*If you'd like to pair program while maintaining social distancing or while working remotely you can try pair programming software like Tuple or the VS code Liveshare extention.
Conclusion
Making small changes to the way you approach tasks when programming can make a monumental difference to readability and maintainability, both for yourself and others. Habits can be hard to break but your future self and your colleagues will probably thank you for it.
If you’ve found this article useful, please follow me on Medium, Dev.to and/ or Twitter.
Top comments (4)
Hey Liam
you can add syntax highlighting like this by using
js
orjavascript
.This will help people to read your code examples.
Awesome, I was unaware, thank you. I've update the snippets.
Yay! Much readable. Thanks!
BTW cool, minimalist title image! 👍
Good points!
Maybe by accident, but all examples show pure functions. Which is another good idea.