6 common mistakes in JavaScript a mid-senior developer should know and be able to tackle
In this post, Bryan, a Lead Software Engineer at EPAM, describes common JavaScript mistakes that Middle and Senior Software Developers should be aware of and know how to handle.
Introduction
There are moments when you look back and remember why you began your journey as a JavaScript Developer and how you keep going and diving deeper into more specific and complex topics. When you look back, it is easy to see some of the common JavaScript mistakes you made as a junior developer and you retain them as part of your deck of common JavaScript errors so you already know how to deal with them.
There is a long list of possible mistakes you could face and things you should be aware of in order to avoid JavaScript bugs, if you are a mid or senior developer and are working with modern JavaScript (latest ES versions to current date October/November 2021). We will briefly mention below just a few of them to help you tackle some important issues and reduce the technical deb and bugs in Javascript.
JavaScript mistake #1: Working without a Coding style guide (rules)
Probably the mother of all bugs in JavaScript begins when you start a project without defining a JS Coding style. It’s very common to have certain rules with your team regarding how to write the JS code in a uniform way. It is important to remember that there are several bad practices and common JavaScript errors that are actually included by default in these styles. If you don’t address this upfront, the code written by the team members will vary considerably and will lead to several errors. These JavaScript bugs will be difficult to notice and be reviewed from a pull request (PR) before being actually merged with the main/master base code.
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:
Standard style
Description: super() must be called before using this.
Link to all the Standard rules: https://standardjs.com/rules.h...
Airbnb style
Rule > 3.4 Use property value shorthand. ESLint: object-shorthand
Why? It is shorter and descriptive.
Code:
Link to all the AirBnB rules: https://github.com/airbnb/Java...
To include these marvelous killers of bugs in JavaScript, the best method is to use the ESLint package. The key, though, is to define with your team which style of code will be chosen and maintained throughout the project.
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.
JavaScript Mistake #2: Using magic values
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
Example:
- with magic values
That approach basically applies to everything that could be considered a constant, not only numbers but to names, prefixes, etc.
JavaScript mistake #3: Disregarding object mutability (or mutating in a cluttered way)
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.
JavaScript mistake #4: Failing to handle errors correctly
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
Link: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Invalid_array_length
- TypeError: 'x' is not iterable
Link:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/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.
But it is important to clarify that an error can actually crash the execution of a script or the call of an event handler, but the correct catch will transform these errors/exceptions (JavaScript throw error) into a handled exception.
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.
More info:
JavaScript mistake #5: Do not use any kind of typing or “static typing”
That could be covered in a separate article, but for now, just know that there are options to avoid some bugs in JavaScript resulting from the absence of 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.
JavaScript mistake #6: Creating memory leaks
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.
Code example:
Link: https://jsbin.com/vetulav/edit?js,console
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:
Link: https://jsbin.com/yulicix/edit?js,console
Or, if possible, reduce the number of maps and cloning of these arrays using a reducer approach like this:
Link: https://jsbin.com/dodoliy/edit?js,console
Link: https://jsbin.com/fosoyoj/edit?js,console
More info about memory management https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management
Wrap-up
There are a lot of potential problems with JavaScript, most of them you can spot with a quick glance, but not all of them. We’ve discussed 5 of the possible mistakes we can fall into as developers. Instead of focusing on specific JavaScript errors, I tried to address general mistakes that are usually familiar after some time in your JavaScript journey.
I strongly recommend the use of linters and static code analyzers as a good strategy to avoid some of the most common JavaScript errors. And give your team sufficient rules to help avoid a lot of mistakes and write uniform and beautiful code.
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.
I also want to mention that there are other things, like the use of design patterns (GoF23), or avoiding anti-patterns, and also some best practices and recommendations of Software Author Gurus like “Clean code” by Robert C Martin, or “Refactoring” by Martin Fowler will help you avoid not only common JavaScript mistakes but also mistakes in any programming language you choose.