Are You Smart Enough To Debug Your Own Code?


Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.


Debugging is twice as hard as writing code. Therefore, if you write the code that you think is the smartest and most clever, you will not be smart enough to debug it. According to this paragraph, we shouldn’t write “smart-aleck” code, otherwise we won’t be able to maintain it ourselves. Good code is something that is easy to understand, something that a fresh graduate intern can understand.


01 Cyclomatic complexity Definition

Cyclomatic complexity, an important concept in software engineering quality measurement, has to be mentioned in order to write easy-to-understand code.

Cyclomatic complexity (CC), also known as conditional complexity, is a measure of code complexity. It was proposed by Thomas J. McCabe, Sr., in 1976 to indicate the complexity of a program. It can be used to measure the complexity of a module judging structure, which can be expressed as the number of independent current paths or the number of least used test cases covering all possible situations.

High cyclomatic complexity indicates that the program code has complex judgment logic and may be of low quality and difficult to test and maintain. Studies have shown a strong correlation between complexity and the number of defects, suggesting that the more complex the code, the more likely it is to go wrong.


02 Cyclomatic complexity calculation

Cyclomatic complexity measures the number of linearly independent paths in a program.

For example, if the program does not contain control, judgment, or conditional statements, the complexity is 1, because the entire program has only one execution path.

IF the program contains an IF statement, there are two paths to execute the entire program, so the complexity is 2;

Two nested IF statements, or one IF statement with two criteria, is 2 * 2 = 4.

For more details, see the following figure:


Computational method of cyclomatic complexity


  1. The cyclomatic complexity can be calculated by using the program control flow diagram as V(G) = e + 2-n

E: Controls the number of edges in the flow diagram

N: Controls the number of nodes in the flow diagram

  1. Cyclomatic complexity corresponds to the number of paths from the starting point to all endpoints in the program control flow diagram, so cyclomatic complexity can also be obtained by counting paths.


03 Reducing Complexity


So the question is, how do we reduce code complexity? If you want to write code that is readable and testable, you can use a powerful tool to reduce code complexity — functional programming.

We’ve already seen that cyclomatic complexity refers to the number of possible branches of a program. Functional programming, on the other hand, tries to avoid all loop statements that branch code execution. Functional programming is a methodology that guides how we write programs. The main idea is to write operations as a series of nested function calls as possible.

One extreme view is that mathematics is the foundation of the physical world, that everything is a mathematical problem, that everything can be represented by mathematical functions. Computer operations can also be regarded as mathematical functions, so our code can be reduced to a series of mathematical expressions.

Functional programming requires only “expressions”, not procedural “statements”. The difference between an expression and a statement is that an expression always returns a value, whereas a statement indicates that an operation has been performed. A function that uses only expressions can be called a “pure function.” The advantage of a pure function is that no matter how many times the function is executed, it does not produce any side effects because it does not modify the intermediate state.

Because we use functional programming, we can replace various conditional statements with expressions, reducing the cyclomatic complexity of our code and making our code readable and testable. There are no for loops in functional programming languages because the logic implies stateful changes. Instead, this loop logic is implemented in functional programming languages recursively, passing functions as arguments, i.e. higher-order functions.

While JavaScript is not a functional language when designed, underscore can be used to make our programs more functional through method libraries like Lodash and underscore. Functional programming is my favorite way to effectively reduce code complexity. But functional programming is beyond the scope of this article and won’t be covered here.

In addition to functional programming, there are more traditional ways to reduce code complexity. Common methods are:

  • Extract function – Separate the independent business or module code, encapsulate and refine it into functions, and interpret the functions of the code through function names to improve the readability of the code. When a function becomes too complex, it should be split into multiple sub-functions that are responsible for independent functions.

  • Alternative algorithms – Complex algorithms increase the likelihood of bugs and understandability/maintainability.

  • Merge conditional expressions – encapsulate complex conditional expressions with functions. Here’s an example:

if(data.code === 200 && data.id && data.list.length > 2) {
    db.insert(data);
} Copy the code

We can encapsulate conditional expressions as functions:

if(isValidate(data)) {
    db.insert(data);
}Copy the code


By doing so, we can keep our code low in code complexity. When we write code, we remember to keep our code Simple Stay Foolish. One way to help you remember this principle is to “write your program with in mind that the person who will be maintaining your program in the future is a severely violent psychopath who knows where you live.”





Follow the wechat official account for more original content