Clean Code JavaScript

Tram Ho

Source

I think the article is so good to read and rewrite, mainly for me and you to write code with principles, easy to read, easy to understand, easy to maintain and reuse code , I have a reference at:

https://github.com/hienvd/clean-code-javascript/

https://github.com/ryanmcdermott/clean-code-javascript

Introduce

Software engineering principles, from Robert C. Martin’s book Clean Code , are applied to the JavaScript language. This is not a tutorial on how to write Javascript code, it is a guide on how to write code that is easy to understand, reuse and refactor in Javascript.

Not all of the principles here must be strictly followed, and even a few of them are in common use. Here, it’s just a guide – nothing more, but it is codified through the experience gained over the years of the author of the book Clean Code.

Software engineering has been in development for more than 50 years, and we are still learning a lot. Once software architecture becomes popular, perhaps then we will have more difficult rules to follow. For now, let these tutorials serve as a standard to evaluate the quality of the JavaScript code that you and your team generate.

Just knowing these guidelines will not immediately make you a better software developer, and working with them for many years does not mean that you will not make any mistakes. Each piece of code begins as a first draft, like clay molded and it will eventually reveal its shape. Finally, we trim the defect when we revisit it with our colleagues. Do not let yourself be defeated by the first draft, which still needs to be edited. Defeat the lines of code instead.

Apply

Out

Using variable names means:

  • Not good :

  • Good:

Use the same vocabulary for the same type of variable:

  • Not good :

  • Good:

Use easy-to-understand and searchable names:

We will read more code than write them. The important thing is that the code we write is readable and searchable. The naming of variables has no semantics compared to the program, we will probably hurt the reader of the code. Make your variable names searchable. Tools like buddy.js and ESLint can help identify unnamed constants .

  • Not good :

  • Good:

In my opinion, there should be a folder to define these shared constants .

Use explanatory variables:

  • Not good :

  • Good:

Avoid harming the brain of others

Until now, not only in programming but everything in life, if clear, of course it would still be easier to understand.

  • Not good :

  • Good:

Do not add unnecessary contexts

If the name of the class , the name of the Object says something, at least what it is, there is no need to repeat it in the variable name.

  • Not good :

  • Good:

It is better to use the default parameters than to check

  • Not good :

  • Good:

Jaw

Function argument (ideally less than or equal to 2)

Number param (parameters) passed in 1 function is extremely important: It makes you at least look better, easier test than, In case of more than 3 params can cause you to test a ton of tests Different cases with separate arguments.

1 or 2 param is ideal for a function , but there are also cases of force majeure, it is best to avoid passing more than 2 params , cases from 3 params , we should combine.

Usually, transferring as many param to the function, the function will increasingly handle more work.

Since Javascript allows creating multiple Objects quickly, without the need for multiple classes , you can use an Object if you need to pass multiple parameters .

To make it clear what a function is expecting, you can use the destructuring structure of ES6 . This has several advantages:

  1. When someone looks at the function , which properties are used will become immediately obvious.
  2. Destructuring also copies the specified initial values ​​of the Object param passed into the function . This can help prevent side effects. Note: Objects and arrays that are destructure from Object param cannot be copied.
  3. Linter will probably warn you about unused properties, which would not have happened without destructuring .
  • Not good :

  • Good:

The function should only solve one problem

This is the most important rule of software engineering. When a function does more than one thing, it becomes more difficult to code, test, and deduce.

When you can isolate a function to perform only one action, it will be easier to refactor and your code will be much more readable. If you only need to follow this guide and do nothing else, you are better than many other developers .

  • Not good :

  • Good:

Function names must say what they do

Just by looking at the function name, you or others understand what the function is for.

  • Not good :

  • Good:

The function should only have an abstract class

When there is more than one abstract class, your function is doing too much. Breaking down functions will make testing and reuse easier.

  • Not good :

  • Good:

Avoid code duplication

Having two functions that handle the same function is a must to avoid, that is, duplicate code . Duplicate code is not good because if you need to change the same logic, you have to fix it in more than one place.

Imagine if you ran a restaurant and you track inventory: including tomatoes, onions, garlic, spices, etc. If you have multiple management lists, all of them must be changed when you serve a dish containing tomatoes. If you only have 1 listing, the update is in one place.

Usually, you have repeated lines of code because you have 2 or more things that differ only slightly, but share many things in common, but their differences require you to have 2 or more separate functions to do many similar things. Erase the identical lines of code means creating an abstraction that can handle file differences with just one function / module or class.

Having the right abstraction is very important, which is why you should adhere to the SOLID principles set out in the class section. Bad abstraction can be worse than duplicated code , so be careful! If you can create a good abstraction , do it! Do not repeat yourself, if you do not want to go to many places updated whenever you want to change something.

  • Not good :

  • Good:

Set default objects with Object.assign

  • Not good :

  • Good:

Do not use flags as function arguments

Flag variables tell your users that the function does more than one thing. The function should only do one task. So separate your functions if they are making branching code based on a boolean variable .

  • Not good :

  • Good:

Do not use flags as function arguments

A function creates side effects if it does anything other than receiving an input value and returning one or more values. Side effects can be recording a file , changing some global variables , or accidentally giving all your money to a stranger.

Now, sometimes you need side effects in a program. As in the previous example, you need to write a file . What you need to do is focus on where you are going to do it. Do not write separate functions and classes to create a specific file . Have a service to write it. One and only.

The main point is to avoid common errors such as sharing state between objects without any structure, using mutable data types that can be written by anything, and not focus where side effects may occur. If you can do that, you’ll be happier than most other developers .

  • Not good :

  • Good:

Avoiding side effects (part 2)

In JavaScript , basic types are passed by value and objects / arrays are passed by reference . In the case of objects and arrays , for example if our function makes a change in a shopping cart array , such as adding a product to purchase, any other function that uses the ‘shopping cart’ array will affected by this addition. This may be fine, but it can also get worse. Imagine the following bad case:

The user clicks “Buy”, the purchase button will call the purchase function, which generates a network request and sends the basket array to the server . Due to a slow connection, the purchase function may keep retrying the request. Now, what if users accidentally clicked the “Add to Cart” button on a product they didn’t really want before the network made a request? If that happens and the network starts sending a request, the buy function will inadvertently add a product because it has a basket array reference that the function that adds the product to the shopping cart has changed by adding a product that they do not want.

A good solution is to add the product to the shopping cart to always make a copy of the basket, change it, and return that copy. This ensures that no function that holds the shopping cart reference is affected by any changes.

Two notes for this approach:

  1. There may be situations where you really want to change the input object, but when you apply this method you will find that these cases are rare. Most problems can be restructured so they no longer have side effects.
  2. Cloning large objects can affect performance. Fortunately, that’s not a big deal in practice because having immutable-js allows this approach to be faster and use less memory than when you manually copy objects and arrays .
  • Not good :

  • Good:

Do not write to global functions

Influencing global variables is a bad practice in JavaScript because you may conflict with other libraries and your API users will not know it until an error occurs on the product.

Think of this example: what if you wanted to extend the Array method in native JavaScript so you could have a diff function that shows the difference between the two arrays ? You could write a new function with Array.prototype , but it might conflict with another library that did the same thing. What if the library only used diff to find the difference between the first and last element of an array ? That’s why it would be much better to just use the ES2015 / ES6 classes and simply extend the Array globally.

  • Not good :

  • Good:

Support function rather than imperative programming

JavaScript is not a functional programming language like Haskell , but it has its function . Functional languages ​​are cleaner and easier to test. Use this programming when you can.

  • Not good :

  • Good:

Packaging conditions

  • Not good :

  • Good:

Avoid negative conditions

  • Not good :

  • Good:

Avoid the conditions

This seems like an impossible task. On hearing this first, most people say, “How do I need to do without the if clause?” The answer is that you can use polymorphism to achieve the same job in a lot of cases.

The second question is usually “That’s good but why do I want to do it?” The answer is the concept we learned earlier: a function should only do one thing. When you have multiple classes and functions that have multiple if clauses, you are telling your users that your function is doing more than one thing. Remember, only one job.

  • Not good :

  • Good:

Avoid type checking (part 1)

JavaScript is not typed, meaning your function can accept any type argument . Sometimes you are tempted by this freedom and easily lead to type checking in your jaw . There are many ways to avoid having to do this. The first thing is to consider using consistent APIs .

  • Not good :

  • Good:

Avoid type checking (part 2)

If you work with basic types like strings , integers, and arrays , and you can’t use polymorphism but you still feel you need to type check, you should consider using TypeScript . It is a great alternative to regular JavaScript , because it provides a static type in addition to the standard JavaScript syntax. The problem with manual type checking is that doing this well requires a lot of lengthy time and this fake “security type” is no substitute for losing the readability of the code. Keep your JavaScript code clean, write good tests and have good code reviews. If not then do all that but with TypeScript (like I said, it’s a good replacement!).

  • Not good :

  • Good:

Do not be too optimal

Modern browsers do a lot of optimization below at run time. A lot of times, if you are optimizing then you are wasting your own time. See here to know when optimization is lacking. Make those optimizations and until they are fixed if possible.

  • Not good :

  • Good:

Remove dead code

Dead code is as bad as duplicate code . There is no reason to keep them in your codebase . If it is not called, discard it! It will still be in your version history if you still need it.

  • Not good :

  • Good:

Objects and Data Structures

Use getter and setter

JavaScript has no interface or type so it is difficult to implement this model, because we do not have keywords like public and private . So using getters and setters to access data on objects is better than simply looking for an attribute on an object . You may ask “Why?”.

Here is a list of reasons why:

  1. When you want to do more than getting an object property, you don’t need to search and change every accessor in your codebase .
  2. Makes adding simple validation when done on a set.
  3. Packing of internal representations.
  4. Easily add logs and handle errors when getting and setting .
  5. Inheriting this class , you can override the default functions .
  6. You can lazy load the properties of an object , get it from the server .
  • Not good :

  • Good:

Make the object private members

This can be done via closures (for ES5 and older).

  • Not good :

  • Good:

Class

Prioritize the ES2015 / ES6 class over the pure ES5 functions

It is difficult to read the inheritance class , constructor class , and method definitions in classic ES5 classes . If you need inheritance (and note that you may not), it is better to use the class . However priority uses smaller function layer until you need large objects and complex.

  • Not good :

  • Good:

Use functions in succession

This is a very useful pattern in JavaScript and you see it in many libraries such as jQuery and Lodash .

It allows your code to be streamlined and concise. For that reason, in my opinion, use the method of sequential functions and let’s see how clean your code is. In class functions , simply return this at the end of each function, and you can chain other methods into it.

  • Not good :

  • Good:

Component precedence over inheritance

As emphasized in the Design Patterns of Gang of Four , you should use component structure rather than inheritance if possible. There are many good reasons to use inheritance as well as use components. The highlight of this motto is that if your mind follows inheritance instinct, think about if the component can better model your problem. In some cases it is possible.

You might be wondering, “When should I use inheritance?” It depends on the issue at your fingertips, but here is a decent list of when inheritance makes more sense than ingredients:

  1. Your inheritance represents each “is-a” relationship and not each “has-a” relationship (Human-> Animal vs. User-> UserDetails).
  2. You can reuse code from the base class (Humans can move like all Animals).
  3. You want to make global changes to derived classes by changing the base class. (Change the calories of all animals as they move)
  • Not good :

  • Good:

SOLID

Single Responsibility Principle

As stated in the Clean Code , “Only one class can be changed for a single reason”. It is fascinating to cram a lot of functions into one layer , just like when you can only get one suitcase for a flight. The problem is that your class will not be conceptually cohesive and there will be plenty of reasons to change. It is important to minimize the number of times you need to change a class . It is important because if there are too many functions in a class and you just want to change a bit of that class , then it may be difficult to understand that changes will affect other modules in the codebase of. how are you.

  • Not good :

  • Good:

Open / Closed Principle

Betrand Meyer said “can comfortably expand a module, but limited amendments within that module.” What does it mean? This principle basically emphasizes that you must allow users to add new functions without changing existing code .

  • Not good :

  • Good:

Liskov Substitution Principle

This is a scary term for a very simple concept. It is formally defined as: “If S is a subtype of T, then objects of type T can be replaced with objects of type S (for example, objects of type S may be replace objects of type T) without changing any of the desired properties of the program (accuracy, task execution, etc.), which is an even more frightening definition. .

The best explanation for this principle is that, if you have a parent class and a subclass, then the base and subclasses can be used interchangeably without altering the correctness of the program. It may still be a little confusing here, so let’s look at the classic Square-Rectangle example below. Mathematically, a square is a rectangle, but if you model this using the “is a” relation through inheritance, you’ll quickly get into trouble.

  • Not good :

  • Good:

Interface Segregation Principle

JavaScript has no interface so this principle does not apply as strictly as other principles. However, it is also important and relevant even with the JavaScript- deficient system.

The principle of interface separation emphasizes that “Users should not be forced to depend on the interfaces they do not use.” Interfaces are hidden constraints in JavaScript because of duck typing .

A good example to illustrate this principle in JavaScript are classes that require the installation of large objects. Not asking the user to set a large number of options is a benefit, because most of the time they don’t need all the settings. Making them optional helps to avoid having a “fat interface”.

  • Not good :

  • Good:

Dependency Inversion Principle

This principle confirms the following two essentials:

  1. But high-level modules should not depend on low-level modules . Both should depend on abstraction .
  2. Abstraction (interface) should not depend on details, but vice versa.

This may be confusing at first, but if you’ve worked with Angular.js, you’ve seen a realization of this principle in the form of Dependency Injection (DI). When they are not the same concepts, DIP keeps its high level modules unaware of its low level modules and sets them up. This can be achieved through DI. A great benefit of DIP is that it reduces the interdependence between modules. Interdependence is a bad pattern, because it makes refactoring difficult.

As asserted earlier, JavaScript has no interface so abstractions that depend on are hidden constraints. That is to say, the methods and properties that one object / class expose to another object / class. In the example below, the implicit constraint is that any Request module for an InventoryRequester will have a requestItems method.

  • Not good :

  • Good:

Testing

Testing is more important than shipping. If you don’t have the test or it’s not enough, every time you ship your code you’re not sure if you will damage anything. It is up to your team to decide what constitutes a sufficient number of tests, but having 100% coverage (all statements and branches) is a way for you to gain high confidence. This means that in addition to having a good framework for testing, you also need to use a good coverage tool .

There is no reason not to write tests. There are many good JS test frameworks , so look for one you like. Once you’ve found the right one for your team, set a goal to always write tests for each of your new features or modules. If your favorite test method is Test Driven Development (TDD), that’s great, but it’s important to make sure you reach your coverage goal before launching a feature or refactor an old feature. somehow.

A unique concept for each unit of test

  • Not good :

  • Good:

Concurrent processing

Use Promise, not callbacks

Callbacks are not so ‘clean’, they cause too much nested code (callback hell). Since ES2015 / ES6, Promise has been included in Javascript. Use them!

  • Not good :

  • Good:

Async / Await is ‘cleaner’ than Promise

Promise is a ‘clean’ alternative to callbacks, but ES2017 / ES8 introduces async and await, which is an even better solution than Promise. What you need to do is a function with the async keyword prefix, and you can write logical commands without a key sequence of functions. Use this if you can take advantage of ES2017 / ES8 features today!

  • Not good :

  • Good:

Error handling

Do not ignore the errors that have been caught

If you do nothing about the error, you will not be able to fix or respond to the error. It is also not much better to write errors to the console (console.log) because most of it can be lost in a bunch of things displayed in the console. If you put any code in a try / catch block, you think an error might occur here, so you should have a solution or create a code stream to handle the error when it happens.

  • Not good :

  • Good:

Do not ignore rejected promises.

Same cause as above.

  • Not good :

  • Good:

Format

The format of the code is subjective. Like many of the rules presented in this document, there are no rigid and quick rules that you must follow. The main point of this section is NEVER PICTURE about how to format the code. There are dozens of tools to automate this. Use a certain tool! It’s a waste of time and money just to argue about code formatting issues.

For things that are not within the scope of automatic code formatting (indentation, tabs and spaces, single and double quotes, etc.) see some instructions here.

Use uniform capitalization

Javascript is an untyped language, so capitalization will tell a lot about your variables, functions, and so on. These rules are subjective, so your team can choose which rules they want. However it is important that no matter how you choose to write, use it consistently in your codebase.

  • Not good :

  • Good:

Calling and calling functions should be near each other

If a function calls another function, keep these functions vertically near the file. Ideally, keep the calling function above the called function. We tend to read code from the top down, just like reading a newspaper. So let’s make our code read that way too.

  • Not good :

  • Good:

Write a caption

Only write notes for things with complex logic.

Comments are usually apologies, not requests. Những đoạn code tốt thì đa số tự nó đã là tài liệu rồi.

  • Không tốt :

  • Tốt :

Đừng giữ lại những đoạn code bị chú thích trong codebase của bạn.

Những công cụ quản lí phiên bản sinh ra để làm nhiệm vụ của chúng. Hãy để code cũ của bạn nằm lại trong dĩ vãng đi.

  • Không tốt :

  • Tốt :

Đừng viết các chú thích nhật ký.

Hãy nhớ, sử dụng công cụ quản lí phiên bản! Chúng ta không cần những đoạn code vô dụng, bị chú thích và đặc biệt là những chú thích dạng nhật ký… Sử dụng git log để xem lịch sử được mà!

  • Không tốt :

  • Tốt :

Tránh những đánh dấu vị trí

Chúng thường xuyên làm nhiễu code. Hãy để những tên hàm, biến cùng với các định dạng thích hợp tự tạo thành cấu trúc trực quan cho code của bạn.

  • Không tốt :

  • Tốt :

End

Tài liệu này cũng có sẵn ở các ngôn ngữ sau:

Share the news now

Source : Viblo