Anyone can write code, but few can write it in a clean and consistent manner. Writing the code is very easy but keeping it consistent and clean is tough enough. Nowadays we usually use different AI tools to generate code, but AI cannot keep track of your consistency. You may use your own conventions for business logic, but different LLM may not be aware of your conventions, so in the decade of AI, it’s much harder to keep track of consistent and clean code.
In this article, we will try to give you an overview of how we can keep our code clean and consistent with using some conventions and best practices.
Node.JS Best Practices
Part 2
Part 4
Testing & Quality Practices
Part 5
Ready to Production Practices
Part 6
Security Best Practices
Part 7
Performance Practices
Part 8
Docker Best Practices
1. Use Linters and Formatters
In our code, we should not keep any unused variables; we can use ” (single quotes) or “” (double quotes) to define a string but to keep the code consistent we must use one string. To keep the code well formatted, we can keep the import section aligned with alphabetically. There are two ways we can put braces in our code e.g.
Or
But the first one is considered a clean one, should we use ; (semicolon) at the end of statement or not, a tab should take how many spaces, should we use trail comma for object or not , should we need to have a blank line at the end of file or not, which files should we automatically format and which are not and much more. If we really want to keep those uses in a consistent manner, we can create some rules while initiating the project. For those tasks we can use two well-known libraries. ESLint and Prettier. These are commonly used libraries for such tasks.
1.1 ESLint
To initiate the rules, the most used library is ESLint. So how can we set up ESLint in the projects and configure rules? In ESLint’s document[1] you will find different ways to install and set up this. As our goal is to define the rule based on projects (not globally), we must install ESLint for our project dependency. Also, clean and consistent code is only necessary for developers, not for production logics, so we will install this package as dev dependency using the –save-dev flag. After successful installation of required packages, we have to create a config file for ESLint where we will define our custom rules that will be applied in our whole project.
1.1.1 eslint.config.js
The ./eslint.config.js file will have our defined rules as follows.
Initially we imported the defineConfig rule and js. The first object of our config define in which type of file the rules will be applied, and from a js plugin we will use predefined recommended rule, rest of the object will be defined as our custom rules as shown in above for unused vars we will show warn, From This document[2] you can learn more about rules for different cases.
1.1.2 .eslintignore
We must not apply rules on some files like .env, docker file, yml, .gitignore, readme.md, node_modules, dist, or any others. To skip the applying rules of eslint’s we can simply make an /.eslintignore file and mention the file name which will be skipped for not applying ESLint rules. After the above, we must add commands in package.json‘s script, so that we can run these commands while we need to check or apply fixes for our rules.
Where the command npm run lint will check all the lint issues and npm run lint:fix will fix all the fixable issues.
1.2 Prettier
To format the code automatically, the most common package is Prettier. Hence, this package is also for code cleaning and consistent formatting. We usually install this dependency as a dev dependency like ESLint. After successfully installation, we will create two files, one is .prettier and another one is .prettierignore
1.2.1 .prettierrc
This file is responsible for what to format and how to format. The bellow’s example will tell you What should be inside this file.
For more configurations, see their official documents[3].
1.2.2 .prettierignore
This file is responsible for which other files should be skipped from being formatted. In our project there might have been some other files such as dockers files, readme files, yml files etc., and we will not want those files to be formatted as our js specific defined rules. So, to skip those formations, we mention those files here in the following way.
After these two above are set up, we will update our package.json‘s script to this.
So, when we run npm run format command it will format our whole code inside the project except mentioned in the /.prettierignore file. Also, for checking if the code is well formatted or not, we can simply run npm run format:check command.
2. Meaningful Naming Conventions
To keep the code readable along with clean, maintainable what we follow for best practice is casing the variables name. There are different types of casing such as camelCase, PascalCase, UPPER_CASE, snake_case, kebab-case etc. These are just conventions made by programmers & not enforced by your languages.
2.1 camelCase
camelCase is a type of convention followed by JavaScript developers where we start writing a variable name with a small case first, then use a single uppercase character to distinguish the words. Similarly, I have written a camel case in section 2.1 title. Mostly, the variables and function names are defined with these conventions. Some developers also use this convention to define file names for services, controllers, routes, etc. e.g. userName , userAddress. In simple projects, people use this convention to file naming.
2.2 PascalCase
PascalCase is another convention of declaring variables. Where the first character starts with a capital case of English alphabets and the rest are in small cases, words are being separated by a capital case. All the class names in JavaScript are mostly declared in this convention. e.g.
In the MVC pattern, the model’s name is written in PascalCase convention.
Also, in React you must see that components names start with PascalCase convention.
2.3 UPPER_CASE
UPPER_CASE convention is where all the alphabets written in capital case and words are being separated by a ‘_’ (underscore). This convention is used in declaring the constant type of variables, something like PI=3.14, EXPIRY=1000 etc. Also, in the .env file we declare the variables in UPPER_CASE conventions.
2.4 snake_case
It’s another convention of variable naming where all the characters in small cases and words are separated by an underscore. It’s less used in the JavaScript community. Languages like go lang, python, etc. use this, also for API response many developers follow this convention. e.g.
2.5 kebab-case
kebab-case is also a convention used in the developer community where all the alphabets written in small case & words are distinguished by a (-) hyphen. This convention is mostly used in renaming the file or folder name. Most of the community prefers this convention for file or folder names.
3. Keep Functions Small & Focused
Writing the code isn’t just writing blindly; while writing codes, we actually define functions. There are some rules and conventions to follow so that function looks clean, maintainable, extendable, and focused. While writing a function, we have to take care of different things as follows below.
- Each function should do one thing only (Single Responsibility).
- Avoid functions longer than 40–50 lines.
- Pass less arguments
- Distinguish function utils, lib, etc.
3.1 Single Responsibility
Single responsibility refers to assigning a single task to a function. The main task of a developer is to find out what the single task is. Most of the time we cannot distinguish between a single task and a combined task. Let me give you an example. Let’s say we have a single dB query which can be used for creating users as well as updating the user. What do we do here? We can write a single function call is saveUser, we put a logic like if(userId) then update, else create.
In this case, it seems that the function is responsible for doing one thing, user savings. But if you look deeply, this function is actually doing two independent tasks using an if logic, and it breaks the single responsibility principle and makes the code hard to read, maintainable. Even though it solved all our business logic and our purpose, it’s totally a bad function. So how could we make this work a single task? We will just make an independent function createUser, updateUser from it something like below.
Now we can call independent functions for independent tasks, and they are actually singly responsible for each single task.
3.2 Limit Function length to 40-60 lines
A function should be as long as your computer screen can show it without scrolling. If the function gets larger, we can create some sub function that supports the large function, doing that we can minimize the number of lines of the function.
3.3 Use Fewer Arguments
Allow a limited number of arguments in a function at most three. If the number of arguments exceeds more than three, pass them as an object instead.
3.4 Avoid Callback Hell
In JavaScript, to handle asynchronous operation we can use the callback function but after certain nested, it will create a callback hell something like below that will lose the readability of code. Just imagine what if there are more dependencies on each level, it will be hell.
So, In the JavaScript community, it is encouraged to use async/await flow with try catch finally in such case, and it will make the code more readable, maintainable and clean.
4. Avoid Magic Numbers & Strings
In our code sometimes we return a status code return {status: 201, data: {}} here the status code is 201 is known as a magic number. This could be something return {status:”OK”, data: {}} here “OK” is also known as a magic string. We should avoid these constants directly written in our codebase. Instead, we can simply create a constants.ts file and define all the magic stuff there in a proper way. e.g.
And later we can directly use them in our code by calling HTTP_STATUS.OK and etc.
5. Consistent API Response Handling
While we are writing the node js code, mostly we write APIs, and it’s very important to keep API response in a consistent format for all the APIs. There are lots of conventions for API response, but the most convenient, clean and well formatted way is using JSON API Response Format which ensures the Richardson Maturity Model (RMM) Level 4 API response format. In NodeJS there are many libraries that are used to format the API Response. jsonapi-serializer is one of them which ensures the Level 4 API response formats API Response for both Success and Error, the response is consistent. Here are examples that could be managed by libraries like.
Success Response
Error Response
6. Follow a Consistent Style Guide
There are some common famous style guides written by some companies that help developers to write consistent code with proper conventions. Airbnb JavaScript Style Guide[4] and Google JS Style Guide[5] are well known among them. So, the team can follow one of them to be consistent in writing node js projects in a consistent way.