When you complain that someone else’s code is difficult to understand, you don’t know that in the near future, you will also become the object of others’ complaints. Change from now on

The company uses Sonarqube to check code quality, and recently started to fix several projects with 1K + bugs and 16K+ odor problems (crashes). The most common problems are:

  • Code specification class errors/style differences: distinguishing between let and const use, square bracket alignment, last function cannot be comma (vue, react), etc

    • As projects get bigger and more people handle them, good code inspection tools like ESlint, Tslint, Prettier can be added to the project to regulate overall code specification and quality
  • The complexity of individual code blocks is high, making them difficult to understand and risky to change

Determining the complexity of a block of code, and how to reduce it, is the focus of our discussion!

Cognitive complexity

A rule definition

Cognitive complexity is a measure of how easy it is to control the flow of functions. Features with high cognitive complexity will be difficult to maintain.

This is sonarqube’s test rule for code complexity. The cognitive complexity of functions should not be too high. As we know, function is the most common code module in native JS, Vue and React, while Sonarqube conducts computational complexity in a function unit. And set a threshold of 15 as the upper limit of the score, that is, the complexity score of a single code block cannot exceed 15.

Sonarqube provides a 21-page PDF that explains the scoring in detail. If you need to see it here, I’ll highlight a few common scenarios in the document

Calculation method

There are three common cases of bonus points:

  • The incremental

    • If, else if, else ternary operators
    • switch
    • for, foreach
    • while, do while
    • catch
    • break LABEL, continue LABEL
    • Binary logic operator sequence (&& and | |) : this is special, behind speak alone
    • Each method in a recursive loop
  • Nesting level (the following structure can add nesting level)

    • If, else if, else ternary operators
    • switch
    • for, foreach
    • while, do while
    • catch
    • Structure of nested methods and similar methods (such as lambda)
// (partial examples)
demo1 (obj) {
  if (obj) { / / + 1
    return true
  }
  
  for (let i = 1; i < 10; i++) { / / + 1
    if (obj.name) { // +2 (if + 1; Nesting + 1)
      if (obj.name.class) { // if +1; Nesting + 2)
        return true
      }
    }
  }
  
  tableData.map(item= > {
    item.name = this.getuserName(item.id)
    item.type = item.type ? Moment(new Date(item.type)).format('YYYY-MM-DD') : ' ' // +1; Nesting + 1)})}Copy the code

In the case of no nesting, a single increment is +1, but if the increment is nested, the increment is accumulated. That is, if the current increment is nested by another increment, the nesting n layer is + N. This part of nesting is the extra points that nesting brings, and as you can imagine, if you have too many nesting layers, it can get very complicated,

First code: single if statement, single increment, +1

Second code: there is a nested structure, for itself increment +1; If the first if statement is nested by fir, then +2 points, of which one point is the additional points brought by nesting; Similarly, the second if statement is +3

Tabledata. map is a structure of nested methods and similar methods, not deltas, not bonus points, but the following triplet is deltas and nesting level, so +2, where one point is the additional value of nesting

/ / binary logic operator sequence (&& and | |)
demo2 (obj) {
  if (obj && obj.name === 'thomas') { / / (+ 2)
    return true
  }
  
  if (obj) { / / + 1
    let flag3 = obj && obj.name === 'lucy' / / + 1
  }
  
  let flag = obj && obj.name === 'nacy' / / + 1
  let flag2 = obj && obj.name || obj.age / / + 2
  let flag3 = obj && obj.name || obj.age && obj.address / / + 3
  let flag4 = obj && obj.name && obj.age && obj.address / / + 1
}
Copy the code

Binary logic operator sequence including && and | |, because it belongs to the incremental, + 1, but it does not belong to the level of nested, so even if it is within the nested layer, there will be no additional nested score.

But it also has a special place is adjacent to the different logical operators will cause extra points, see the example:

First code: a single if statement, +1; Single &&, +1

The second piece of code: nested by an if statement, but still +1

Third code: the first line has only one &&, +1; The second row && adjacent is | |, because the adjacent logical operators, different causes && + 1, | | + 1, the final score of 2; Same for the third line, && +1; | | + 1, && + 1; In line 4, all the logical operators have no different logical operators next to each other, so the whole thing is +1

To help you understand, here’s a complicated example

function fetch (id) {
  $page.getSomething({ id }).then(res= > {
    if (res && res.data && res.data.userList || res.data.infoList) { // +1 (if +1; && + 1; | | + 1; Nesting + 1)
      if (this.$route.query.flag === 'one') { // if +1; Nesting + 2)
        for (let [k, v] of Object.entries(userList)) { // +1 (if +1; Nesting + 3)
          this.$set(v, '$flag', k)
          for (let [m, n] of Object.entries(this.ee)) { // +1 (if +1; Nesting + 4)
            this.$set(n, '$flag', m)
            if (v.emplid === n.emplid) { // +1 (if +1; Nesting + 5)
               emplList.push(v)
            }
          }
        }
      } else if (this.$route.query.flag === 'two') {/ / (if + 1; Nesting + 1)
        console.log('two')}}this.retrieve(res.data)
    const formNo = res.data.bpmsFormNo
    this.retrieveHistory(formNo)
    this.isLoading = false
  }).catch((a)= > { / / + 1
    this.isLoading = false})}Copy the code

If you don’t understand the details of the bonus points, please ask questions in the comments section, and I will answer them when I see them.

The above example is intended only as a demo to understand the bonus point algorithm. The actual project code is much more complex than this, and would be twice as disgusting.

Reduce cognitive complexity

The sample code is just an idea, and rewriting is not necessary. It’s up to you to decide if rewriting is more appropriate

  • Statements with nested structures receive additional bonus points for nesting. If the current statement is simple and nested, you can rewrite it in the following way

  • The logical operators in a line of code should be unique, not different logical operators at the same time.

  • Replace complex redundant conditional judgments with switch statements

  • Functions should follow the principle of uniformity, do not have a function to implement the function is too complex, only do one thing. How to abstract out multiple functions and refactor them according to the business scenario

  • If the judgment logic is complex, replace the judgment condition with function, and replace the execution function with function. Finally, string several functions with tervariate operator, on the one hand, enhance the semantic of the code, on the other hand, improve the readability of the code. It is suggested to encapsulate the execution logic that is complex into a function

  • Prefacing the end of a function’s judgment condition statement makes it easier to understand and reduces unnecessary code execution

conclusion

I hope you found this article helpful,

If there are any mistakes in this article, please correct them in the comments section. If this article has helped you, please like it and follow it.