×
Community Blog Did I Really Write This Mess of Code?

Did I Really Write This Mess of Code?

An Alibaba expert discuss his thoughts on what is good code and what makes code bad.

By Ma Feixiang, nicknamed Zepan at Alibaba.

1

Have you ever encountered this kind of situation? After a few weeks or months after writing some code, you go to look over it again and feel as though it's not even proper code, and then doubt your abilities as a coder.

At least with my team, we deal with code every day. However, when asked what is good code, many people give unfocused or unclear explanations, rather than a simple and clear definition. Today we will discuss what good code is.

In a word, the only valid measurement of code quality is WTFs/min, as noted by Robert C. Martin.

2
Image source: https://www.osnews.com/story/19266/wtfsm/

Robert's understanding of what makes code good is interesting and truly inspiring to me. In addition to being used for machine execution that produces the expected results, the code we wrote is more often read by people, which may later be O&M personnel. It is even more often read by ourselves after a period of time.

Any fool can write code that computers understand. Good programmers write code that humans can understand. -- Martin Fowler

So, when it comes to good code, the first word that jumps to my mind is clean. Good code must be clean, giving readers a pleasant feeling.

Clean code reads like well-written prose.-- Grady Booch

What Are the Features of Good Code

It is pretty difficult to define what is good code. I believe that many people, like me, do not think that clean code is necessarily good, but good code must be clean. So, "cleanliness" is a necessary condition for good code. Clean code must feature high cohesion and low coupling, and must be readable and easy to maintain.

High Cohesion and Low Coupling

High cohesion and low coupling is a term that's familiar to any programmer's ears, but this term is a bit too broad to get down deep into the bottom of things. Smart programmers have put forward several object-oriented design principles to measure the positives and negatives of code. These include:

  • The open-closed principle (OCP)
  • The single responsibility principle (SRP)
  • The dependency inversion principle (DIP)
  • The least knowledge principle (LKP)/Law of Demeter
  • The Liskov substitution principle (LSP)
  • The interface segregation principle (ISP)
  • The composite/aggregate reuse principle (CARP)

These principles must be familiar to everyone, and are guidelines when we write code. Code developed based on these principles features high cohesion and low coupling. In other words, we can use these principles to measure the quality of code.

However, these principles are not rigidly dogmatic. We often violate or abandon some principles because of other trade-offs, such as readability and complexity. For example, if a subclass has a feature, we may violate the Liskov substitution principle. To provide another example, the single responsibility principle sometimes conflicts with the interface segregation principle. In this case, we often abandon the interface segregation principle and maintain the single responsibility principle. Code that we write with violation of one or more of these principles is not necessarily bad as long as there are sufficient reasons for the violation.

Readability

Is high cohesion and low coupling enough for code? Not necessarily. I think code must be easy to read. Good code should have a readable style, structure, and design. You can consider the following aspects to make the code more readable.

| Naming

Naming is a very serious matter. You need to consider good names for a project, package, class, method, variable, parameter, or even a temporary variable.

  • Veritable: A good name must be veritable and does not need to be commented out for readers to understand its meaning.
/**
* 创建后的天数
**/
int d;
int daysSinceCreation;

As shown above, the latter name is much better than the former, and the reader can immediately understand the meaning of the variable.

  • Easy to distinguish: It is common for us to write very similar method names, and it is impossible to distinguish between them by only their names, for example, getAccount() and getAccountInfo(). In this case, it is difficult for you to determine which one to use for a call, and you need to check the implementation code.
  • Readable: The name must be readable. You should not invent abbreviations yourself or use a mixture of languages, like Chinese and English.
  • Concise: The shorter the name the better. In fact, as long as the meaning is delivered sufficiently, a shorter name is always better.

| Format

Good code formatting is also an important part of readability. The code format can be vertical or horizontal.

  • Vertical format: Generally, only one expression or clause is written in each row. A set of code represents a complete idea, and different sets of code are separated by blank lines.
public class Demo {
    @Resource
    private List<Handler> handlerList;
    private Map<TypeEnum, Handler> handlerMap = new ConcurrentHashMap<>();

    @PostConstruct
    private void init() {
        if (!CollectionUtils.isEmpty(handlerList)) {
            for (Handler handler : handlerList) {
                handlerMap.put(handler.getType(), handler);
            }
        }
    }

    public Result<Map<String, Object>> query(Long id, TypeEnum typeEnum) {
        Handler handler = handlerMap.get(typeEnum);
        if (null == handler) {
            return Result.returnFailed(ErrorCode.CAN_NOT_HANDLE);
        }
        return handler.query(id);
    }
}

If the blank lines are removed, the readability is greatly reduced, as shown below.

public class Demo {
    @Resource
    private List<Handler> handlerList;
    private Map<TypeEnum, Handler> handlerMap = new ConcurrentHashMap<>();
    @PostConstruct
    private void init() {
        if (!CollectionUtils.isEmpty(handlerList)) {
            for (Handler handler : handlerList) {
                handlerMap.put(handler.getType(), handler); } } }
    public Result<Map<String, Object>> query(Long id, TypeEnum typeEnum) {
        Handler handler = handlerMap.get(typeEnum);
        if (null == handler) {
            return Result.returnFailed(ErrorCode.CAN_NOT_HANDLE);
        }
        return handler.query(id); }
}

Class static variables and entity variables should be defined at the top of the class. Methods in a class are defined in the following order: the public or protection method > private method > the getter or setter method.

  • Horizontal format: Proper indentation and spacing is also needed.
  • Consistency within a team: Generally, the code format within a team should be as consistent as possible. Alibaba Group has detailed Java coding guidelines.

| Classes and Functions

  • Classes and functions should be as short as possible: Classes and functions should not be too long. Alibaba Group requires that the length of a function not exceed 80 lines. Overly long functions are not readable and often contain a large amount of duplicate code.
  • A function only performs actions at the same level: Each execution statement of a function should be at the same level of abstraction. For example, we often write a function that needs to assign values to a data transfer object (DTO), call an API operation, and then return the result. The function covers three steps: value assignment to a DTO, a call of an API operation, and the return of the results. If the function also contains the specific operations for assigning values to a DTO, the execution statements of this function are not abstracted at the same level.
  • It is better for a function to have fewer parameters: The more parameters a function has, the more difficult it is to call. Therefore, make sure that a function has as few parameters as possible. A function preferably has none parameters.

| Comments

  • Refactoring bad code rather than commenting out it: Comments cannot improve bad code. Before trying to comment out code, consider whether you can adjust the structure and name to improve it, which often in turn makes the comments unnecessary.
  • Providing good comments that contain information, intentions, explanations, and warnings: We often encounter a situation where the execution logic of the code that is written in a comment does not match the logic of the actual code. The reason is that most of the time, the code has been changed, but the comments have not been changed accordingly. Therefore, it's good to provide some additional information that the code does not have in comments to show your design intentions, rather than writing about the specific implementation.
  • Removing commented code: Version control tools, such as Git, have recorded the code change history for us. It is unnecessary to keep outdated code, and the code with comments will interfere with reading.

| Error Handling

  • Error handling is very important, but it cannot interfere with the code logic: Error handling should be concentrated at the same layer. Moreover, the function for error handling is used to handle error messages and should not contain any other business logic code.
  • When your code throws an exception, provide enough environment information and statements to facilitate troubleshooting: When an exception is thrown, it is best to make sure the execution class name, key data, and environment information are also thrown. In this case, the custom exception class can be used to handle the exception at a unified layer, helping you quickly and easily locate problems.
  • Special case pattern can be used for the elimination of exception control or null judgment: Most of the exceptions are of the NullPointerException (NPE) type, and sometimes such an exception can be eliminated through a null object.
  • "null" should not be passed or returned if possible: "null" should not be passed or returned to minimize the occurrence of NPE.

How to Determine That Code Is Not Good

Now that we have discussed the necessary conditions for good code, let's take a look at the negative conditions for what makes code not so great. In other words, let's dive right into what makes code bad code. Kent Beck uses smell to describe the timing of refactoring. In my opinion, when the code smells bad, this also represents that it is not good code.

Bad Smell in Code

Duplicate code is the root of all evil in software design. --Robert C. Martin

  • Duplication: Martin Fowler also believes that duplicate code is the first cause of bad taste. Most of the time, after eliminating duplicate code, we can find that the code becomes much cleaner.
  • Overly long functions, excessively large classes, and overly long parameters: Overly long functions are hard to interpret, hard to share, and have poor selection capabilities and are also hard to maintain.

An excessively large class always covers various actions and it has too much duplicate code.

If a parameter is too long, it may be difficult to understand and cause errors when being called.

  • Divergent changes, shotgun surgery, and feature envy: If a class does not have a single responsibility, different changes may be made to the single class, which indicates that there are divergent changes. You should consider separating these changes.

If a single change is made to the methods of multiple classes simultaneously, shotgun surgery exists. You should consider putting the methods to be modified in the same class.

Also, if a function uses the methods of another class to excess, feature envy exists. In this case, you should consider moving the function to the class to which it should belong.

  • Data clumps: Sometimes three or four identical fields appear in multiple classes and functions. When this happens, it is necessary to create a class for the set of fields and encapsulate the fields.
  • Excessive if-else or switch statements: You should consider replacing large numbers of if-else or switch statements with polymorphism. Some even think that, except for individual cases, if-else statements should not exist in code.

Summary

This article first summarizes the necessary condition for good code, which I think is cleanliness. Then it analyzes the characteristics of clean code, and finally explores the negative conditions for good code by looking at what makes code bad. These are only my opinions, of course.

I hope this article can help you in future programming and perhaps maybe also inspired you to tackle on your coding mistakes of past.

0 0 0
Share on

zepan

1 posts | 0 followers

You may also like

Comments

zepan

1 posts | 0 followers

Related Products