So the idea is to use a linter to ensure that all members of the team follow these rules and avoid common JS mistakes. Here is an example of Standard and AirBnB style code:
Description: super() must be called before using this.
Link to all the Standard rules: https://standardjs.com/rules.h...
Rule > 3.4 Use property value shorthand. ESLint: object-shorthand
Why? It is shorter and descriptive.
Link to all the AirBnB rules: https://github.com/airbnb/Java...
Your decision will be configured using the ESLint rules, or you could base it on current renamed JS style guides like Standard, AirBnB, or Google.
Additionally, it’s critical to see if that was successfully integrated with the IDE used by all the team members in order to verify whether or not the written code has style errors. A better approach might be to use an automatic tool to refactor the code according to the rules like Prettier (probably the unopinionated tool for that purpose).
ESLint link https://eslint.org/
Prettier Link: https://prettier.io/docs/en/integrating-with-linters.html
A bonus here could be the use of code analyzers, also known as static code analyzers. Some of the most killer features are the ability to:
- check code smells (sometimes checked with linters but sometimes not)
- check code complexity
One of the most used code analyzers is probably SonarQube, but there are others that can do a good job too.
Using magic values is a common mistake in multiple languages, but in JS it’s definitely a mistake you should avoid. First of all, a magic value is considered a constant value that magically appears in the code, and probably only those who wrote the code could easily explain what it does, and usually, even they forgot it. XD
- with magic values
- the right way
That approach basically applies to everything that could be considered a constant, not only numbers but to names, prefixes, etc.
Const does not imply that the object is not mutable, as some people assume.
The issue with mutability does not end here. If you want to be more strict with mutability, there are many other considerations to keep in mind.
For example, the use of this mutability approach requires extra space because it involves several clones of mutated data. Although this approach is commonly used in Functional Programming approaches, it is important to be aware of the spatial complexity to avoid memory leaks, as will be discussed in mistake #6.
Handling errors correctly is not limited to using the try-catch statement. It is very important to recognize how to use the exceptions and how to catch and handle the errors in a clever and safe way.
To do so it’s important to recognize there are some differences between an Error and an Exception, even though r most of the time, JS developers refer to them as if they are practically the same.
An Error can be considered as an unexpected response of the program. An Error can happen in different phases, for example during compilation time (usually Syntax errors), or running time also called runtime errors.
Examples of execution errors in JS:
- RangeError: invalid array length
- TypeError: 'x' is not iterable
In addition to runtime errors, there is another type called logical errors, which are related to how the program is expected to work.
Exceptions are considered errors that are expected or almost expected to be handled in order to avoid the interruption of the flow of the program execution.
Example of an exception catch and handling:
This is a good example of how to handle an error as an exception using the specific type of error to handle specific types of bugs in a certain way, and others differently.
Other kinds of errors include syntax errors and type errors, which are usually prevented using linters, as mentioned above, or using the strict mode, and transpilers (like Babel for example).
The need to customize error types allows for better handling of these special cases into tailor-made exceptions. Since ES6 there is an easy way to do it, extending the Error Object and creating custom error types.
The first one is to use Typescript. Since TS was released, its main objective was to provide static typing to JS using several syntax sugar features to guarantee the types should be maintained and for developers more familiar with other languages. It’s important to mention that TS is basically a superset of JS, which means, you could use JS inside TS because TS is JS too, but with types and some syntax sugar added.
There are other options, like using Flow directly with Vanilla JS, and more rudimentary approaches using JSDocs as part of the IDE, which is basically using JS with the types working like a lint inside the IDE.
A memory leak is easy to recognize at runtime when you monitor your server instances and see the average memory consumption increase. The first thing someone might think of doing is increasing the default memory limit of Node JS from the 1.4Gb using the classic environment variable modification like this:
But that never solves the main question, which is “why is the memory consumption so high”?
There are several articles regarding this situation, and here, I will cover just one very common case based on my experience.
You probably made a “functional” code, which usually works well for a basic unit test, but what if you included only the happy pad test cases, and some edge cases, could that be the cause of the memory leaks?.
That’s the point of using arrays to manage collections, saving in-memory massive collections like arrays to iterate them afterward.
The example above is a good way to show how with a map we can perform a simple transformation for a map, but cloning it. For a little array, the memory consumption is insignificant, but with greater arrays, we would easily find a big memory impact.
Solution: Understand that you are exceeding your space complexity for big arrays (this is a CS concept we didn’t cover here but that could be covered in another article). A good idea is to split in chunks the arrays you are working with. Identify possible extra copies of those arrays, understanding where a no mutation approach with a map would be better, or where an in-place transformation would be best (unless you have to do it with an FP approach).
So, another good option is to chain the maps, avoiding multiple clonings:
Or, if possible, reduce the number of maps and cloning of these arrays using a reducer approach like this:
In addition, static typing could be a good choice, bearing in mind that the implementation of static typing could be progressive and could help you avoid a lot of headaches with a minimum amount of effort.
Keep in mind that these suggestions are not a silver bullet. There are a number of other possible JS mistakes that are harder to recognize. Only experience, constant learning, and a good domain of JS particularities can detect, as well as a good error/exception handling strategy, which is certainly the second most important advice to keep in mind.