Node.js has been widely adopted over the last few years, even by such well-known giants as Netflix, IBM, Walmart, Intel and NASA. Benefits like a support plan and a vast collection of open-source libraries account for much of the growth in Node adoption in the enterprise, replacing typical enterprise solutions like Java and .Net. Node.js went from a common technology in the startup world to a mainstream development approach used by companies of all sizes.
Node.js is easy to learn, especially if you already know Javascript. And maintaining it can be less tiresome than some other languages. However, when you are a developer with a strong background i
Lack of Testing and Linting
Node.js is an interpreted language, which is highly useful. The f
- Test using tools such as Mocha or tape;
- Consider the use of
linters such as ESLint and standard; - Measure the test coverage using a tool like
istanbul ; - Consider creating a coding style for your project;
- Check out Clean Code Javascript and best practices.
Lack of modularity
The require command is always "require once", after you import a file for the first time. Node uses a cache and will always return the same object. So, there's no need to create huge files with big functions:
- Start by refactoring repeatedly used code into a function;
- Try to think about modules following the SOLID principle.
Global values
The "globals" variables must be avoided. Consider removing from your code the keyword global.
global.myNumber = 5; //Global variable initialized to value 5.
Global values beside the config.js
For example, it is acceptable to use the proccess.env.NODE_ENV on the configuration file. However, the other modules should avoid
Synchronous code after initialization
When your application is starting, you can use methods like `fs.readDirSync`, `fs.readFileSync`, `fs.accessSync`, `fs.changeModSync`, `fs.chownSync`, `fs.closeSync` and `fs.existsSync`. However, when the application is ready, you can't call synchronous methods. When you execute a synchronous function, you will block the event loop.
Function Arguments
Long List ArgumentTry to avoid creating functions with a long list of arguments. Consider the following function:
function createProduct(id, name, description, sku, colors, sizes, price, imageURL, thumbnailURL){ // code... // code... }
Optional Arguments
Many libraries write functions using optional arguments. However, when you write functions for your application with some business rules, it's a common necessity to evolve and make changes. So, you should avoid creating functions with optional arguments because it is hard to evolve with more arguments.
Ignoring Error Statement
If you ignore an Error Statement, it can lead to unexpected behavior: namely, always returning when calling callbacks. The following code has one problem because, if an error occurs, then the execution will not stop, but will continue.
/* missing a RETURN statement */ fs.readFile(`${filePath}`, (err, data) => { if (err) { console.log(`Error reading file: ${filePath}`) } //... //code //... }Be careful when “parsing”
The Node.js provides a rich API to handle I/O asynchronous operations. You should be careful when parsing huge
Callback Hell
In JavaScript, over-usage of callback functions often leads to complex chains of functions. It is hard to understand the flow of execution. Many times, code ends up looking like a lot of nested callbacks:
fs.readdir(dir, (err, files) => { if (err) { return console.log(`Error finding files: ${err}`) } else { files.forEach( (fileName, fileIndex) => { let filePath = path.resolve(dir, fileName); fs.readFile(`${filePath}`, (err, data) => { if (err) { return console.log(`Error reading file: ${filePath}`) } let params = { Key: fileName, Body: data }; s3bucket.upload(params, (err, data) => { if (err) { return console.log(`There was an error uploading to AWS S3 the file: ${filePath} at ${bucket}/${fileName} - ${err}`) } fs.unlink(filePath, (err) => { if (err) { return console.error(`There was an error deleting the file ${filePath} - ${err}`) } }) }) }) }) } })
Before you go further on advanced topics, you can resolve that by keeping your code shallow and still
async function start(){ const files = await readDirAsync(dir) files.forEach( async (fileName, fileIndex) => { const filePath = path.resolve(dir, fileName) const data = await readFileAsync(`${filePath}`) const params = { Key: fileName, Body: data }; await s3UploadAsync(params) await unlinkAsync(filePath) }) }
Keep in mind that you should wrap await in try/catch to capture and handle the errors in awaited promises. Besides that, you should also consider using the generator or promises.
Conclusion
In summary, these are some of the most common "anti-patterns" to avoid, or at least be aware of when working in Node. My hope is that this helps someone who is starting on Node.js. If you are starting with software-as-a-service apps, consider reading The Twelve FactorApp.
I also highly recommend checking out DevMedia’s DevCast: Introducing Node.js. In this video, you’ll learn about the purpose of Node.js, discover the advantages of its asynchronous, event-oriented programming model, and understand how it differs from object-oriented/multi-paradigm languages such as PHP, C #, Python, and others. Let us know what you think in the comments below!
Author
Wellington Soares
Wellington Soares is a Software Engineer at Avenue Code. He has extensive experience in the development of medical systems and holds a Bachelor's degree in Computer Science from Federal University of Santa Catarina. He is interested in: AWS, JavaScript, Node.js, DICOM, C ++, Qt, React and Webpack.