The reason for writing this article is:

Recently, because the company’s business development was too busy and tight, all the developers had to work overtime to catch up with the project and many new people joined, resulting in a series of uncontrollable quality problems of the code.

This article gives an overview and collation of the various problems in the code during this period, mainly focusing on the problems of code coding, abstraction, as well as the problems related to the invocation and writing interfaces in microservices.

In fact, according to the truth, these should belong to the basic skills of programming, it seems not worth writing an article, but it can be started by these basic skills, to discuss the essence of a code programming system construction, so it is worth to expand.

The discussion will be carried out one by one according to a principle and suggestion

First, the problem of coding

1. Avoid excessive IF nesting

So-called “arrow” code is largely because of a lot of the IF nested cause, on the one hand, forming a deep arrow, cause the indentation exaggerated in reading the code blocks, and even more crucial is too deep nesting level cause code logic complexity deepen, when reading to N layer nested don’t clear what is the logic to enter, Significantly reduces code readability and maintainability.

The main reason for if-else being too long is simply to check the current state and decide whether to continue or jump.

1) Use Guard Clauses to return in advance to avoid nesting layers

IF/ELSE we can use it in two ways:

“Give priority to meet the conditions and proceed with the processing process.”

If (user.getid () == 10){if(user.getid () == 10){else{if(user.getid () == 10)}Copy the code

“Prioritise failure to meet conditions and allow them to logically exit the process”

if(user.getId() ! }else{// do not satisfy the condition, execute}Copy the code

This is two different logical structure, they can write the same code logic, but in the first, if the code quantity increase, nested increased, it is easy to lost their way in the condition, if use the second way to write the condition, in turn, can early exit type logic early exit, so that you can free the arrow type code. As follows:

2) Plan the judgment conditions and state model

If it is a business permit is actually can integrate multiple judgment conditions, so that we can avoid the occurrence of arrow code, but only for a period of the if conditional statements and become very bloated line are not put, if there is a very complicated multiple state judgment and combination, can use “status table”, or the state machine design pattern for decoupling, etc.

3) Abstract the business details in IF into functions

Separating the tedious business details in IF into functions can reduce the long and smelly code on the one hand, and is more conducive to masking details and locking the business logic that is not relevant to the process in a specific area.

It is also good for code reading, focusing on the flow of the business rather than the details of the business implementation, and using functions to encapsulate and abstract the code.

2. Be careful of operations in multi-layer loop nesting

Sometimes it is true that nesting of several layers of for loops is necessary for business implementation, but we need to be careful that the number of innermost loops executed is multiplied by the number of layers of loops.

For example, the code went through four levels of loops, and if the loop was 10x10x10x10, the final DB operation would have gone through 10,000 separate overhead.

First, the 10,000 overhead is fine if the programmer knows clearly that the overhead is a business necessity when writing the code, but the programmer is not aware of this point when writing the code will be magnified at any time

Second, even if 10000 times overhead is a business necessity, there is still room for optimization according to this code. All query conditions can be pieced together in the loop, and then batch query can be carried out to a certain extent, which can greatly reduce DB overhead.

3. Do not arbitrarily define local variable names

Naming style we can refer to Ali’s “Java Development Manual”, where it is mainly pointed out that the phenomenon of random naming of local variables is quite serious, we generally think that local variables are only used in this method, and will not affect other methods and others.

However, it is not known that the bad or arbitrary naming of local variable names can also cause problems for developers, and even cause mistakes that they do not know. The following is a classic example of arbitrary naming of variables:

The variable names MA and map have no intrinsic meaning, and their generic classes are the same, so it’s hard to guarantee that you won’t accidentally use errors in the code below.

4. Avoid long, smelly classes and methods

It is no exaggeration, I have seen a class of more than 1000 lines, a method is 300 lines long, IDE is about a page normally 30-50 lines (depending on the screen size), this is called the reader how to view.

When reading, the continuous scroll page, even the original author, I’m afraid it is difficult to control this class for a long time, let alone the later maintainer.

More importantly, if a class method is too long, it can seriously hinder your ability to extend and modify it. Every logic in a method involves many scattered contexts, making it extremely difficult to modify and extend it.

According to Refactoring, when a class is too long, it is often due to unclear responsibilities. There are dozens of methods in a class, which is definitely too many responsibilities or not subdivided responsibilities.

Here’s a quick list of refactorings for long, smelly refactorings:

  1. Analysis needs to refactor the functionality of the class
  2. Extract methods with the same responsibilities into separate classes by composition or integration
  3. Analyze the methods and extract the repetitive code into functions
  4. Naming. Having a good name for a class helps to locate and establish responsibility for the class

5. Log logs provide specific information to facilitate location

The log should have a clear direction, one can assist debugging, one can record events, and locate errors

In the following example, a log.error log is printed, but even if we check the log afterwards, we only know that there is an error log, but we don’t know which user log and which coupon log it is, which can not help us directly locate the error.

Take a look at the log and print the returned List directly. Printing here does nothing to preserve or locate the problem, but leaves worthless information and clutters the log.

Usually, we leave the entity name and logical keywords to identify a record.

6. Complex modules, code untouched, outline annotation first

It’s as hard to stop a beginner programmer from writing code in the first place as it is to stop a hungry person from trying to eat.

In the process and system design, we have E-R diagrams and flow charts to help us build models and processes.

When we come across a class or method with complex logic, we also need to sort out the logic and flow first, using comments or pseudocode to define the logic and flow, after establishing the overall idea, put up a skeleton, and then fill in the flesh (write code). As long as the flow is clear and the logic is clear, writing code at this point is actually the easiest thing to do.

7. The same functions should be abstracted as far as possible, and not diverged

As an example of our construction order, see the following figure:

The single backend uses a design pattern of adaptor, mainly wrapping the same interface to the outside world, and then implementing the separation of classes according to the situation (commodity logic)

It is a good intention to unify the logic and package it as a unified interface to expose it to the outside world. However, in this example, the separation of commodity logic is only concerned, while ignoring the fact that the logic, such as inventory, payment, coupon and so on, is actually unified and can be abstracted.

As a result, for example, the coupon logic needs to be modified three times at the same time

As can be seen from the figure above, the premature use of adaptation mode separated the business at the entrance, resulting in the subsequent separation of business codes with the same logic. The logic of “inventory deduction”, “coupon deduction” and “payment” should be the same, but three sets of codes were also used for maintenance.

2. Microservice coding

1. The RPC interface must be a service responsibility

The RPC interface is a micro-service producer that provides certain capabilities for consumers to use. At this time, the RPC interface should not be defined as a large and complete interface

It was found before that some students defined RPC interface as:

insertXXX 

updateXXX 

listXXX
Copy the code

This is tantamount to moving the DAO layer directly to RPC, exposing the entire DAO directly, which violates the interface call principle of microservices. RPC interfaces provide only the most atomic functions, limiting the use of consumers in producer-defined businesses.

2. Do not call the RPC interface repeatedly

Different from the in-project programming, the invocation of each RPC interface is accompanied by a one-time network overhead, and repeated requests to an interface are required. In this case, the provider of the RPC interface can be required to provide another interface that can be used in batches to change the single repeated request into a single request, reducing the network overhead.

Third, use tools to help clean up malicious code

1. The P3C plug-in

In the use of Eclipse or IDEA programming, the first use of Ali’S P3C plug-in for assistance, code specification check plug-in P3C, is according to the “Alibaba Java Development Manual” transformed into an automatic plug-in.

2. Use Skywalking to find malicious codes

Unlike P3C directly assisted coding, SkyWalking can track a link in a production environment to determine if a microservice’s interface performance or mobilization is abnormal.

This is not a trivial introduction to the use of Skywalking, but link tracing is not just a concern for operations or architects. It is also a way for developers to track their code and see how it performs in a production environment from a higher perspective.

Good at using link tracing can often find problems that are ignored in ordinary coding. For example, a careless loop call to RPC can easily lead to a large call span, and developers are often not aware of it in time in programming

Four, summary

The principles of abstraction and the use of some design patterns are also discussed in the sharing, so it is unnecessary to repeat them here.

Simply put, to write good performance, high readability, logical code, often rely on not curd, but the usual summary and thinking.